유가증권 (Deribit Options Delta Dynamic Hedging Strategy)

저자:리디아, 창작: 2022-11-01 17:49:07, 업데이트: 2023-09-15 20:49:52

img

FMZ Quant이 제시하는 전략은 Deribit Options Delta Dynamic Hedging 전략, 또는 DDH (동적 델타 헤지) 전략입니다.

옵션 거래에 대한 연구를 위해, 우리는 일반적으로 이러한 개념을 마스터해야합니다:

· 옵션 가격 모델, B-S 모델, 옵션 가격은 [대상 가격], [전력 사용 가격], [시효까지 남은 시간], [실용된 변동성] 및 [위험 없는 금리]를 기준으로 결정됩니다.

· 옵션 노출:

- 델타 옵션의 방향 위험. 델타가 +0.50이라면, 해당 상품 가격이 상승 또는 하락할 때 수익 또는 손실을 발생시키는 옵션은 0.50 스팟으로 간주 될 수 있습니다. -가마 방향 리스크 가속화. 예를 들어, 콜 옵션의 경우, 가마 때문에, 델타는 가격의 상승을 계속하기 때문에 가격에서 0.50에서 +1.00로 점차 이동합니다. - 테타 시간 노출. 당신이 옵션을 구입할 때, 만약 대상 물가가 변하지 않는다면, 지나가는 매일마다, 당신은 테타 금액에 표시된 수수료를 지불합니다 (데리빗은 USD로 표기됩니다). 옵션 판매시, 만약 상품 가격이 변하지 않는다면, 당신은 매일 테타 금액에 표시된 수수료를 받게 됩니다. - 베가 변동성 노출. 당신이 옵션을 구입할 때, 베가는 긍정적, 즉 긴 변동성입니다. 암시 변동성이 증가하면, 당신은 베가 노출에서 이익을 얻습니다. 그리고 반대로, 당신이 옵션을 판매 할 때, 당신은 암시 변동성이 감소함에 따라 이익을 얻습니다.

DDH 전략 설명:

· DDH 원칙 설명 위험중립적인 거래 방향은 옵션과 선물의 델타를 일치시키는 것으로 달성됩니다. 옵션의 델타가 대상 가격의 변화를 따라가기 때문에 선물과 스팟의 델타는 변하지 않습니다. 옵션 계약에 포지션을 취하고 미래에셋 수장을 통해 델타를 균형 잡은 후, 전체 델타는 대상 가격의 움직임에 따라 다시 불균형됩니다. 옵션과 미래에셋 포지션의 이러한 조합은 델타를 동등시키기 위해 지속적인 동적 헤지링을 필요로합니다.

· 예를 들어: 우리가 콜 옵션을 구매할 때, 우리는 이 시점에서 긴 방향으로 지위를 보유하고 있습니다. 이 시점에서, 전체 델타 중립 (0 또는 0에 가깝게) 에 도달하여 옵션의 델타를 헤지하기 위해 선물을 단축해야합니다. 옵션 계약의 만료까지 남은 시간, 변동성 및 다른 요인을 무시하자. 시나리오 1: 주식의 가격이 상승하면 옵션의 델타가 증가하고 전체 델타가 긍정적 인 수치로 이동하고 선물은 다시 헤지해야합니다. 퓨처스를 계속 짧게하기 위해 몇 개의 짧은 포지션을 열어 전체 델타가 다시 균형을 이루도록하십시오. (재균형 전에 옵션의 델타는 크지만 선물의 델타는 상대적으로 작습니다. 콜 옵션의 한계 이익은 계약 단편 지점의 한계 손실을 초과하고 전체 포트폴리오가 수익을 낼 것입니다.)

시나리오 2: 소재의 가격이 떨어지면 델타의 옵션 부분이 감소하고 전체 델타는 음수로 이동하여 짧은 미래에셋 지점의 일부를 폐쇄하고 전체 델타를 다시 균형으로 만듭니다. (이 시점에서 재균형 전에 옵션의 델타는 작지만 선물의 델타는 상대적으로 크다. 콜 옵션의 한계 손실은 계약 단위 포지션의 한계 이익보다 작으며 전체 포트폴리오는 여전히 이윤을 올릴 것입니다.)

따라서 이상적인 상태에서는 시장의 변동이 있을 때까지 그 소재의 증가와 감소가 이익을 가져다 줄 것입니다.

하지만 고려해야 할 요소도 있습니다. 시간적 가치, 거래 비용 등.

그래서 지후의 핫샷의 설명은 인용됩니다:

The focus of Gamma Scalping is not on delta, dynamic delta hedging is just a way to avoid underlying price risk in the process.
Gamma Scaling focuses on alpha, which is not the alpha of stock selection. Here, alpha=gamma/theta, that is, how much gamma is exchanged for the time loss of unit Theta.
This is the point of concern. It is possible to construct a portfolio that floats both up and down, but it must be accompanied by time loss, and then the problem lies in the cost effectiveness.

Author: Xu Zhe
URL: https://www.zhihu.com/question/51630805/answer/128096385

DDH 전략 설계 설명

· 종합 시장 인터페이스 포괄, 프레임 디자인 · 전략 UI 설계 · 전략적 상호 작용 설계 · 자동 헤지 기능 설계 소스 코드:

// Construct functions
function createManager(e, subscribeList, msg) {
	var self = {}
    self.supportList = ["Futures_Binance", "Huobi", "Futures_Deribit"]  // of the supported exchanges

    // Object attributes
    self.e = e
    self.msg = msg
    self.name = e.GetName()
    self.type = self.name.includes("Futures_") ? "Futures" : "Spot"
    self.label = e.GetLabel()
    self.quoteCurrency = ""  
    self.subscribeList = subscribeList   // subscribeList : [strSymbol1, strSymbol2, ...]
    self.tickers = []                    // All market data obtained by the interface, define the data format: {bid1: 123, ask1: 123, symbol: "xxx"}}
    self.subscribeTickers = []           // The required market data, define the data format: {bid1: 123, ask1: 123, symbol: "xxx"}}
    self.accData = null 
    self.pos = null 

    // Initialize the function
    self.init = function() { 
    	// Judge if the exchange is supported
        if (!_.contains(self.supportList, self.name)) {        	
        	throw "not support"
        }
    }

    self.setBase = function(base) {
        // Switching base address for switching to analog bot
        self.e.SetBase(base)
        Log(self.name, self.label, "switch to analog bot:", base)
    }

    // Judging data precision
    self.judgePrecision = function (p) {
        var arr = p.toString().split(".")
        if (arr.length != 2) {
            if (arr.length == 1) {
                return 0
            }
            throw "judgePrecision error, p:" + String(p)
        }
        
        return arr[1].length
    }

    // Update assets
    self.updateAcc = function(callBackFuncGetAcc) {
        var ret = callBackFuncGetAcc(self)
        if (!ret) {
        	return false 
        }
        self.accData = ret 
        return true 
    }

    // Update positions
    self.updatePos = function(httpMethod, url, params) {
        var pos = self.e.IO("api", httpMethod, url, params)
        var ret = []
        if (!pos) {
            return false 
        } else {
            // Organize data
            // {"jsonrpc":"2.0","result":[],"usIn":1616484238870404,"usOut":1616484238870970,"usDiff":566,"testnet":true}
            try {
                _.each(pos.result, function(ele) {
                    ret.push(ele)
                })
            } catch(err) {
                Log("Error:", err)
                return false 
            }
            self.pos = ret
        }
        return true 
    }

    // Update the market data
    self.updateTicker = function(url, callBackFuncGetArr, callBackFuncGetTicker) {
    	var tickers = []
    	var subscribeTickers = []
    	var ret = self.httpQuery(url)
    	if (!ret) {
    		return false 
    	}
    	// Log("test", ret)// test
    	try {
            _.each(callBackFuncGetArr(ret), function(ele) {
            	var ticker = callBackFuncGetTicker(ele)
            	tickers.push(ticker)
                if (self.subscribeList.length == 0) {
                    subscribeTickers.push(ticker)
                } else {
                	for (var i = 0 ; i < self.subscribeList.length ; i++) {                        
                    	if (self.subscribeList[i] == ticker.symbol) {
                    		subscribeTickers.push(ticker)
                    	}
                	}
                }
            })
        } catch(err) {
        	Log("Error:", err)
        	return false 
        }

        self.tickers = tickers
        self.subscribeTickers = subscribeTickers
        return true 
    }

    self.getTicker = function(symbol) {
    	var ret = null 
    	_.each(self.subscribeTickers, function(ticker) {
    		if (ticker.symbol == symbol) {
    			ret = ticker
    		}
    	})
    	return ret 
    }

    self.httpQuery = function(url) {
    	var ret = null
        try {
            var retHttpQuery = HttpQuery(url)
            ret = JSON.parse(retHttpQuery)
        } catch (err) {
            // Log("Error:", err)
            ret = null
        }
        return ret 
    }

    self.returnTickersTbl = function() {
        var tickersTbl = {
        	type : "table", 
        	title : "tickers",
        	cols : ["symbol", "ask1", "bid1"], 
        	rows : []
        }
        _.each(self.subscribeTickers, function(ticker) {        
        	tickersTbl.rows.push([ticker.symbol, ticker.ask1, ticker.bid1])
        })
        return tickersTbl
    }
    
    // Back to the position table
    self.returnPosTbl = function() {
        var posTbl = {
            type : "table", 
            title : "pos|" + self.msg,
            cols : ["instrument_name", "mark_price", "direction", "size", "delta", "index_price", "average_price", "settlement_price", "average_price_usd", "total_profit_loss"], 
            rows : []
        }
        /* Format of the position data returned by the interface
        {
            "mark_price":0.1401105,"maintenance_margin":0,"instrument_name":"BTC-25JUN21-28000-P","direction":"buy",
            "vega":5.66031,"total_profit_loss":0.01226105,"size":0.1,"realized_profit_loss":0,"delta":-0.01166,"kind":"option",
            "initial_margin":0,"index_price":54151.77,"floating_profit_loss_usd":664,"floating_profit_loss":0.000035976,
            "average_price_usd":947.22,"average_price":0.0175,"theta":-7.39514,"settlement_price":0.13975074,"open_orders_margin":0,"gamma":0
        }
        */
        _.each(self.pos, function(ele) {
        	if(ele.direction != "zero") {
                posTbl.rows.push([ele.instrument_name, ele.mark_price, ele.direction, ele.size, ele.delta, ele.index_price, ele.average_price, ele.settlement_price, ele.average_price_usd, ele.total_profit_loss])
            }
        })
        return posTbl
    }

    self.returnOptionTickersTbls = function() {
        var arr = []
        var arrDeliveryDate = []
        _.each(self.subscribeTickers, function(ticker) {
            if (self.name == "Futures_Deribit") {
                var arrInstrument_name = ticker.symbol.split("-")
                var currency = arrInstrument_name[0]
                var deliveryDate = arrInstrument_name[1]
                var deliveryPrice = arrInstrument_name[2]
                var optionType = arrInstrument_name[3]

                if (!_.contains(arrDeliveryDate, deliveryDate)) {
                    arr.push({
                        type : "table", 
                        title : arrInstrument_name[1],
                        cols : ["PUT symbol", "ask1", "bid1", "mark_price", "underlying_price", "CALL symbol", "ask1", "bid1", "mark_price", "underlying_price"], 
                        rows : []
                    })
                    arrDeliveryDate.push(arrInstrument_name[1])
                }
                // Iterate through arr
                _.each(arr, function(tbl) {
                    if (tbl.title == deliveryDate) {
                        if (tbl.rows.length == 0 && optionType == "P") {
                            tbl.rows.push([ticker.symbol, ticker.ask1, ticker.bid1, ticker.mark_price, ticker.underlying_price, "", "", "", "", ""])
                            return 
                        } else if (tbl.rows.length == 0 && optionType == "C") {
                            tbl.rows.push(["", "", "", "", "", ticker.symbol, ticker.ask1, ticker.bid1, ticker.mark_price, ticker.underlying_price])
                            return 
                        }                        
                        for (var i = 0 ; i < tbl.rows.length ; i++) {
                            if (tbl.rows[i][0] == "" && optionType == "P") {
                                tbl.rows[i][0] = ticker.symbol
                                tbl.rows[i][1] = ticker.ask1
                                tbl.rows[i][2] = ticker.bid1
                                tbl.rows[i][3] = ticker.mark_price
                                tbl.rows[i][4] = ticker.underlying_price
                                return 
                            } else if(tbl.rows[i][5] == "" && optionType == "C") {
                                tbl.rows[i][5] = ticker.symbol
                                tbl.rows[i][6] = ticker.ask1
                                tbl.rows[i][7] = ticker.bid1
                                tbl.rows[i][8] = ticker.mark_price
                                tbl.rows[i][9] = ticker.underlying_price
                                return 
                            }
                        }
                        if (optionType == "P") {
                            tbl.rows.push([ticker.symbol, ticker.ask1, ticker.bid1, ticker.mark_price, ticker.underlying_price, "", "", "", "", ""])
                        } else if(optionType == "C") {
                            tbl.rows.push(["", "", "", "", "", ticker.symbol, ticker.ask1, ticker.bid1, ticker.mark_price, ticker.underlying_price])
                        }
                    }
                })
            }
        })
        return arr 
    }

    // Initialization
    self.init()
	return self 
}


function main() {
    // Initialization, clear logs
    if(isResetLog) {
    	LogReset(1)
    }

    var m1 = createManager(exchanges[0], [], "option")
    var m2 = createManager(exchanges[1], ["BTC-PERPETUAL"], "future")

    // Switch to analog bot
    var base = "https://www.deribit.com"
    if (isTestNet) {    
        m1.setBase(testNetBase)    
        m2.setBase(testNetBase)
        base = testNetBase
    }

    while(true) {
        // Options
        var ticker1GetSucc = m1.updateTicker(base + "/api/v2/public/get_book_summary_by_currency?currency=BTC&kind=option", 
            function(data) {return data.result}, 
            function(ele) {return {bid1: ele.bid_price, ask1: ele.ask_price, symbol: ele.instrument_name, underlying_price: ele.underlying_price, mark_price: ele.mark_price}}) 
        
        // Perpetual futures
        var ticker2GetSucc = m2.updateTicker(base + "/api/v2/public/get_book_summary_by_currency?currency=BTC&kind=future", 
            function(data) {return data.result}, 
            function(ele) {return {bid1: ele.bid_price, ask1: ele.ask_price, symbol: ele.instrument_name}})
        if (!ticker1GetSucc || !ticker2GetSucc) {
            Sleep(5000)
            continue
        }

        // Update positions
        var pos1GetSucc = m1.updatePos("GET", "/api/v2/private/get_positions", "currency=BTC&kind=option")
        var pos2GetSucc = m2.updatePos("GET", "/api/v2/private/get_positions", "currency=BTC&kind=future")

        if (!pos1GetSucc || !pos2GetSucc) {
            Sleep(5000)
            continue
        }

        // Interactions
        var cmd = GetCommand()
        if(cmd) {
            // Handle interactions
            Log("Interaction commands", cmd)
            var arr = cmd.split(":")
            // cmdClearLog 
            if(arr[0] == "setContractType") {
                // parseFloat(arr[1])
                m1.e.SetContractType(arr[1])
                Log("exchanges[0] contract set by exchange object.", arr[1])
            } else if (arr[0] == "buyOption") {
                var actionData = arr[1].split(",")
                var price = parseFloat(actionData[0])
                var amount = parseFloat(actionData[1])
                m1.e.SetDirection("buy")
                m1.e.Buy(price, amount)
                Log("execution price: ", price, "execution amount: ", amount, "execution direction: ", arr[0])
            } else if (arr[0] == "sellOption") {
                var actionData = arr[1].split(",")
                var price = parseFloat(actionData[0])
                var amount = parseFloat(actionData[1])
                m1.e.SetDirection("sell")
                m1.e.Sell(price, amount)                
                Log("execution price: ", price, "execution amount: ", amount, "execution direction: ", arr[0])
            } else if (arr[0] == "setHedgeDeltaStep") {
                hedgeDeltaStep = parseFloat(arr[1])
                Log("set the parameter hedgeDeltaStep:", hedgeDeltaStep)
            } 
        }
        
        // Obtain the future contract prices
        var perpetualTicker = m2.getTicker("BTC-PERPETUAL")
        var hedgeMsg = " PERPETUAL:" + JSON.stringify(perpetualTicker)

        // Obtain the total delta value from the account data        
        var acc1GetSucc = m1.updateAcc(function(self) {
        	self.e.SetCurrency("BTC_USD")        
        	return self.e.GetAccount()
        })
        if (!acc1GetSucc) {
        	Sleep(5000)
        	continue
        }
        var sumDelta = m1.accData.Info.result.delta_total

        if (Math.abs(sumDelta) > hedgeDeltaStep && perpetualTicker) {
            if (sumDelta < 0) {
                // Hedging futures go short if delta is greater than 0                 
                var amount = _N(Math.abs(sumDelta) * perpetualTicker.ask1, -1)                
                if (amount > 10) {
                    Log("Exceed the hedging threshold, current total delta:", sumDelta, "Buy futures")
                    m2.e.SetContractType("BTC-PERPETUAL")                    
                    m2.e.SetDirection("buy")
                    m2.e.Buy(-1, amount)
                } else {
                	hedgeMsg += ", hedging order volume less than 10"
                }
            } else {
                // Hedging futures go long if delta is less than 0
                var amount = _N(Math.abs(sumDelta) * perpetualTicker.bid1, -1)
                if (amount > 10) {
                    Log("Exceed the hedging threshold, current total delta:", sumDelta, "Sell futures")
                    m2.e.SetContractType("BTC-PERPETUAL")
                    m2.e.SetDirection("sell")
                    m2.e.Sell(-1, amount)
                } else {
                	hedgeMsg += ", hedging order volume less than 10"
                }
            }
        }

        LogStatus(_D(), "sumDelta:", sumDelta, hedgeMsg, 
        	"\n`" + JSON.stringify([m1.returnPosTbl(), m2.returnPosTbl()]) + "`", "\n`" + JSON.stringify(m2.returnTickersTbl()) + "`", "\n`" + JSON.stringify(m1.returnOptionTickersTbls()) + "`")
        Sleep(10000)
    }
}

전략 주소:https://www.fmz.com/strategy/265090

전략 작전:

img

이 전략은 학습에 초점을 맞춘 튜토리얼 전략입니다. 실제 봇에서 조심스럽게 사용하세요.


관련

더 많은