期权十八般武器之两种保护性期权策略

论坛 期权论坛 期权     
期权匿名问答   2022-1-7 19:54   10508   0
一、保护性期权策略

我们前面介绍期权基本策略时讲到,预期趋势会上涨时,可以买入看涨期权,预期趋势会下跌时,可以买入看跌期权,如果方向判断反了,最大亏损为权利金,如果方向判断对了,收益则无限。
但我们知道期权的德尔塔值小于1,即标的合约波动一个点,期权的价格波动小于1点,所以在同样的趋势空间里,期权的波动幅度小于标的合约的波动幅度,期权的盈利也会小于持有标的合约产生的盈利。
如果把标的合约和期权相结合,即能限定风险,又能发挥标的合约带来的利润优势,就比单独采用期权买方策略更好,保护性期权策略就是这样的组合。
例如,预期趋势会上涨,我们买入标的合约多头,为防下跌风险,我们可以同时买入看跌期权,下跌后看跌期权行权获得标的合约空头,与标的合约多头形成锁仓,便锁定了最大亏损,如果上涨,看跌期权弃权,标的合约多头可以赚取上涨利润,这样的组合便是保护性看跌期权,即用看跌期权保护标的合约多头,同时不限制标的合约多头的利润。
保护性看涨期权的情景则相反。
保护性期权策略和基本的期权买方策略一样,都有着损失有限盈利无限的特征,相比于基本的期权买方策略,保护性期权策略需要支付标的合约的保证金,投入资金更多些,所以如果不想投入更多资金,就采用基本的期权买方策略,如果想发挥行情的波动优势,就采用保护性期权策略。
保护性期权策略的盈亏结果图如下:


二、示例程序

保护性期权做的是趋势,所以我们沿用上篇文章示例的趋势信号,突破布林线上轨时,买入标的合约,同时买入平值的看跌期权,跌破布林线下轨时,卖出标的合约,同时买入平值的看涨期权。标的合约采用主力合约。
由于买入的是平值期权,最大亏损便锁定为权利金,我们以利润达到一倍权利金时止盈,盈亏比1比1,由于期权还不便于在量化中行权,可以考虑在临近到期时全部平仓,也可以止损达到最大时平仓。
以下是示例代码:
#!/usr/bin/env python
#  -*- coding: utf-8 -*-
import asyncio
from pathlib import Path
from datetime import datetime, time, date, timedelta
import os, json,pandas
from tqsdk import TqApi, TqKq,TqAuth, TqAccount, TqChan
from tqsdk import TqSim, TqBacktest, BacktestFinished
from tqsdk.tafunc import time_to_str,time_to_datetime,ema,ma
from tqsdk.ta import EMA,BOLL
flog = os.getcwd()+'\logs'  #程序所在目录下创建logs目录
print(flog) #输出记录路径
if os.path.isfile(flog) != True:
    p=Path(flog)
    p.mkdir(exist_ok=True) #创建路径
if os.path.isfile(flog+'\PositionLog.json') != True: #
    with open(flog+'\PositionLog.json','w+',encoding='utf-8') as Position_Log:
        json.dump({},Position_Log)  
    Position_Log=open(flog+'\PositionLog.json','r+',encoding='utf-8')
else : Position_Log=open(flog+'\PositionLog.json','r+',encoding='utf-8') #
PositionDict = json.load(Position_Log)    #期权持仓字典

#开平仓函数,该函数可查看文章《交易系统与凯利公式》或天勤教程18.5节
async def OpenClose(api,quote={},position={},kaiping='',lot=0,price=None,advanced=None,order_close_chan=None,trade_chan=None,
                    block=True,n_price_tick=0,che_time=0,order_info='无',signal_price=float('nan')):
    '''返回值[成交手数, 成交均价, 是否撤单, 交易品种, 交易方向]'''

async def cta(api,product_id,kline_period=15*60,boll_period=20,p=1.618):
    kline_short = await api.get_kline_serial(symbols_info[product_id]["major"],kline_period,200) #标的合约K线
    quote = api.get_quote(symbols_info[product_id]["major"]) #主力合约盘口行情
    async with  api.register_update_notify() as update_chan :
        async for _ in update_chan :
            boll_short = BOLL(df=kline_short,n=boll_period,p=p) #
            Smid_up =  boll_short.iloc[-1].mid > boll_short.iloc[-3].mid
            Smid_down =  boll_short.iloc[-1].mid <  boll_short.iloc[-3].mid
            #布林张开
            boll_open = boll_short.iloc[-1].top > boll_short.iloc[-3].top and boll_short.iloc[-1].bottom < boll_short.iloc[-3].bottom
            cross_up = kline_short.iloc[-1].close > boll_short.iloc[-3].top
            cross_down = kline_short.iloc[-1].close < boll_short.iloc[-3].bottom
            buy_up = boll_open and Smid_up and cross_up #看涨信号
            sell_down = boll_open and Smid_down and cross_down #看跌信号

            if  buy_up and len(PositionDict[product_id]["BUY"]) < 2 : #看涨信号,无持仓
                option_p = await api.query_atm_options(quote.underlying_symbol,quote.last_price,symbols_info[product_id]["price_level2"],"PUT")
                if option_p and option_p[0]: #查询到看跌期权
                    position_f = api.get_position(quote.underlying_symbol)
                    quote_f = await api.get_quote(quote.underlying_symbol)
                    position_p = api.get_position(option_p[0])
                    quote_p = await api.get_quote(option_p[0])
                    if quote_p.expire_rest_days >=10 :#期权未到期,买期货,买看跌期权
                        while position_f.pos_long < symbols_info[product_id]["lots"]:
                            buy_open = api.create_task(OpenClose(api,quote_f,position_f,"kaiduo",symbols_info[product_id]["lots"]-position_f.pos_long,"对手价","FAK"))
                            await buy_open #
                        else: #下单完成,更新持仓字典
                            PositionDict[product_id]["BUY"].update({"FUTURE":quote.underlying_symbol})
                        while position_p.pos_long < symbols_info[product_id]["lots"]:
                            buy_open = api.create_task(OpenClose(api,quote_p,position_p,"kaiduo",symbols_info[product_id]["lots"]-position_p.pos_long,"对手价","FAK"))
                            await buy_open #
                        else:
                            PositionDict[product_id]["BUY"].update({"PUT":option_p[0]})
            elif len(PositionDict[product_id]["BUY"]) == 2 :#有看涨持仓
                position_f = api.get_position(PositionDict[product_id]["BUY"]["FUTURE"])
                position_p = api.get_position(PositionDict[product_id]["BUY"]["PUT"])
                quote_f = await api.get_quote(PositionDict[product_id]["BUY"]["FUTURE"])
                quote_p = await api.get_quote(PositionDict[product_id]["BUY"]["PUT"])
                #剩余3天到期,或浮盈达到权利金值,平仓
                if quote_p.expire_rest_days <=3 or position_f.float_profit_long + position_p.float_profit_long >= \
                    position_p.open_cost_long:
                    while position_f.pos_long:
                        sell_close = api.create_task(OpenClose(api,quote_f,position_f,"pingduo",position_f.pos_long,"对手价","FAK"))
                        await sell_close
                    else:PositionDict[product_id]["BUY"].pop("FUTURE")
                    while position_p.pos_long:
                        sell_close = api.create_task(OpenClose(api,quote_p,position_p,"pingduo",position_p.pos_long,"对手价","FAK"))
                        await sell_close
                    else:PositionDict[product_id]["BUY"].pop("PUT")

            if sell_down and len(PositionDict[product_id]["SELL"]) < 2 : #看跌信号,无持仓
                option_c = await api.query_atm_options(quote.underlying_symbol,quote.last_price,symbols_info[product_id]["price_level1"],"CALL")
                if option_c and option_c[0]: #查询到看涨期权
                    position_f = api.get_position(quote.underlying_symbol)
                    quote_f = await api.get_quote(quote.underlying_symbol)
                    position_c = api.get_position(option_c[0])
                    quote_c = await api.get_quote(option_c[0])
                    if quote_c.expire_rest_days >=10 :#期权未到期,卖期货,买看涨期权
                        while position_f.pos_short < symbols_info[product_id]["lots"]:
                            sell_open = api.create_task(OpenClose(api,quote_f,position_f,"kaikong",symbols_info[product_id]["lots"]-position_f.pos_short,"对手价","FAK"))
                            await sell_open #
                        else: #下单完成,更新持仓字典
                            PositionDict[product_id]["SELL"].update({"FUTURE":quote.underlying_symbol})
                        while position_c.pos_long < symbols_info[product_id]["lots"]:
                            buy_open = api.create_task(OpenClose(api,quote_c,position_c,"kaiduo",symbols_info[product_id]["lots"]-position_c.pos_long,"对手价","FAK"))
                            await buy_open #
                        else:
                            PositionDict[product_id]["SELL"].update({"CALL":option_c[0]})  
            elif len(PositionDict[product_id]["SELL"]) == 2 :#有看跌持仓
                position_f = api.get_position(PositionDict[product_id]["SELL"]["FUTURE"])
                position_c = api.get_position(PositionDict[product_id]["SELL"]["CALL"])
                quote_f = await api.get_quote(PositionDict[product_id]["SELL"]["FUTURE"])
                quote_c = await api.get_quote(PositionDict[product_id]["SELL"]["CALL"])
                #剩余3天到期,或浮盈达到权利金值,平仓
                if quote_c.expire_rest_days <=3 or position_f.float_profit_short + position_c.float_profit_long >= \
                    position_c.open_cost_long:
                    while position_f.pos_short:
                        sell_close = api.create_task(OpenClose(api,quote_f,position_f,"pingkong",position_f.pos_short,"对手价","FAK"))
                        await sell_close
                    else:PositionDict[product_id]["SELL"].pop("FUTURE")
                    while position_c.pos_long:
                        sell_close = api.create_task(OpenClose(api,quote_c,position_c,"pingduo",position_c.pos_long,"对手价","FAK"))
                        await sell_close
                    else:PositionDict[product_id]["SELL"].pop("CALL")
   
            Position_Log.seek(0) #
            Position_Log.truncate() #
            json.dump(PositionDict, Position_Log,indent=2) #
            Position_Log.flush() #持仓字典实时保存本地

#标的合约信息
symbols_info = {
                "m":{
                    "lots":20,           #开仓手数
                    "price_level1":0,    #期权档位,默认平值
                    "price_level2":0,    #期权档位,默认平值
                    "product_id":"m",    #合约id
                    "major":"KQ.m@DCE.m"  #主力代码
                    }
                }
product_ids = ["m"] #品种列表
#PositionDict的结构为
#{"product_id":{"BUY":{"CALL":"期权代码","FUTURE":"期货代码"},"SELL":{"PUT":"期权代码","FUTURE":"期货代码"}}}
for product_id in product_ids:
    if product_id not in PositionDict:
        PositionDict.update({"m":{"BUY":{},"SELL":{}}}) #
api = TqApi(auth=TqAuth("信易账户", "密码"))
account = api.get_account()
positions = api.get_position()
orders = api.get_order()
api.create_task(cta(api,"m"))   #
while True:
    api.wait_update()
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:400157
帖子:80032
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP