La couverture dynamique par delta des options sur dérivés

Auteur:Je suis désolée., Créé à partir de: 2022-04-24

La couverture dynamique par delta des options sur dérivés

Cette fois, la stratégie de FMZ Quant estLa couverture dynamique par delta des options sur dérivés, abrégé en DDH.

Pour l'étude du trading d'options, nous avons généralement besoin de maîtriser les concepts dans plusieurs aspects:

  • Modèle de tarification de l'option; modèle B-S; le prix de l'option est déterminé sur la base de prix sous-jacent, prix d'exercice, journées à l'expiration, volontalité implicite et taux d'intérêt sans risque.

  • Expositions au risque d'option:

    • Si la valeur delta est de +0,5, le résultat des profits et pertes de l'option lorsque le prix sous-jacent augmente et diminue peut être considéré comme un spot de 0,50.
    • Gamma la vitesse accélérée du risque directionnel. Par exemple, une option d'achat. En raison de Gamma, à partir du moment où le prix sous-jacent est au prix d'exercice, Delta sera proche de +1.00 à partir de +0.50, dans le processus d'augmentation du prix.
    • Lorsque vous achetez des options, si le prix sous-jacent reste constant, à chaque jour qui passe, vous paierez une charge affichée par la valeur Theta (Deribit est au prix en USD). Lorsque vous vendez des options, et le prix sous-jacent reste constant, à chaque jour qui passe, vous recevrez une commission affichée par la valeur Theta.
    • Vega exposition à la volatilité. Lorsque vous achetez des options, Vega est exprimée sous forme d'une valeur positive, à savoir la volatilité implicite à long terme. Lorsque la volatilité implicite augmente, vous pouvez faire un profit en étant exposé à Vega. La situation inverse est également la même. Lorsque vous vendez des options, la volatilité implicite diminue et vous aurez des bénéfices.

Explication de la stratégie DDH:

  • Explication du principe DDH En équilibrant le delta des options et des contrats à terme, la neutralité du risque de la direction de négociation est atteinte. Après avoir détenu une position sur un contrat d'option et utilisé des contrats à terme pour couvrir et équilibrer le delta, à mesure que le prix sous-jacent change, le delta global apparaîtra à nouveau déséquilibré. Pour la combinaison de positions d'options et de positions à terme, une couverture dynamique constante est nécessaire pour équilibrer le delta.

    Par exemple: Lorsque nous achetons une option d'achat, nous avons une position haussière. à ce moment, il est nécessaire de court terme à terme pour couvrir l'option Delta pour atteindre la neutralité globale Delta (0 ou proche de 0). Nous ignorons les facteurs tels que les jours à l'expiration et la volatilité implicite du contrat d'option. Scénario 1: Lorsque le prix sous-jacent augmente, l'option Delta augmente et le Delta global passe à un nombre positif. (Avant le rééquilibrage, le delta de l'option est important, le delta des contrats à terme est relativement faible, le bénéfice marginal de l'option d'achat dépasse la perte marginale du contrat court et l'ensemble du portefeuille générera un profit.) Scénario 2: Lorsque le prix sous-jacent chute, le delta de l'option diminue, et le delta global passe à un nombre négatif, et certaines positions futures courtes sont fermées pour rétablir le solde global du delta. (Avant le rééquilibrage, le delta de l'option est faible, le delta des contrats à terme est relativement important, la perte marginale de l'option d'achat est inférieure au bénéfice marginal du contrat court et l'ensemble du portefeuille sera toujours rentable.)

    Par conséquent, idéalement, la hausse et la baisse du sous-jacent apportent tous deux des profits, tant que le marché fluctue.

    Cependant, d'autres facteurs doivent être pris en considération: valeur temporelle, coûts de négociation et autres.

    J'ai cité l'explication d'un maître de Zhihu:

    Le scalping gamma n'est pas axé sur le delta, le scalping delta dynamique est juste un moyen d'éviter le risque de prix sous-jacent dans le processus. Le scalping gamma se concentre sur l'alpha. L'alpha n'est pas l'alpha de la sélection des actions. Ici, l'alpha = gamma/theta, c'est-à-dire combien de gamma est échangé par la décadence temporelle de l'unité theta. C'est le point. Il est possible de construire une combinaison de hausse et de chute à la fois avec des bénéfices flottants, certainement accompagnés de décadence temporelle, et le problème est le rapport de performance des coûts. Auteur: Xu Zhe; lien vers l'article original:https://www.zhihu.com/question/51630805/answer/128096385

Conception de la stratégie DDH

  • l'encapsulation de l'interface de marché agrégée, la conception de la structure;
  • conception de l'interface utilisateur stratégique;
  • la conception de l'interaction stratégique;
  • conception de la fonction de couverture automatique.

Le code source:

// constructor 
function createManager(e, subscribeList, msg) {
	var self = {}
    self.supportList = ["Futures_Binance", "Huobi", "Futures_Deribit"]  // from the supported platforms

    // 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 as: {bid1: 123, ask1: 123, symbol: "xxx"}}
    self.subscribeTickers = []           // the market data in need; define the data format as: {bid1: 123, ask1: 123, symbol: "xxx"}}
    self.accData = null 
    self.pos = null 

    // initialization function 
    self.init = function() { 
    	// judge whether the platform is supported 
        if (!_.contains(self.supportList, self.name)) {        	
        	throw "not support"
        }
    }

    self.setBase = function(base) {
        // switch base address, used to switch to the simulated bot 
        self.e.SetBase(base)
        Log(self.name, self.label, "switch to simulated bot:", base)
    }

    // judge the 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 {
            // arrange 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
    }
    
    // return the positon 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 : []
        }
        /* the position data format 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])
                }
                // traverse 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 
    }

    // initialize 
    self.init()
	return self 
}


function main() {
    // initialize, and vacuum logs
    if(isResetLog) {
    	LogReset(1)
    }

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

    // switch to the simulated 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
        }

        // interaction 
        var cmd = GetCommand()
        if(cmd) {
            // process interaction 
            Log("interactive command:", cmd)
            var arr = cmd.split(":")
            // cmdClearLog 
            if(arr[0] == "setContractType") {
                // parseFloat(arr[1])
                m1.e.SetContractType(arr[1])
                Log("exchanges[0] sets contract:", 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("executed price:", price, "executed amount:", amount, "executed 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("executed price:", price, "executed amount:", amount, "executed direction:", arr[0])
            } else if (arr[0] == "setHedgeDeltaStep") {
                hedgeDeltaStep = parseFloat(arr[1])
                Log("set hedgeDeltaStep:", hedgeDeltaStep)
            } 
        }
        
        // obtain futures contract price 
        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) {
                // delta value is more than 0, hedge futures and make short                   
                var amount = _N(Math.abs(sumDelta) * perpetualTicker.ask1, -1)                
                if (amount > 10) {
                    Log("exceeding the hedging threshold value, the current total delta:", sumDelta, "call futures")
                    m2.e.SetContractType("BTC-PERPETUAL")                    
                    m2.e.SetDirection("buy")
                    m2.e.Buy(-1, amount)
                } else {
                	hedgeMsg += ", hedging order amount is less than 10"
                }
            } else {
                // delta value is less than 0, hedge futures and make long
                var amount = _N(Math.abs(sumDelta) * perpetualTicker.bid1, -1)
                if (amount > 10) {
                    Log("exceeding the hedging threshold value, the current total delta:", sumDelta, "put futures")
                    m2.e.SetContractType("BTC-PERPETUAL")
                    m2.e.SetDirection("sell")
                    m2.e.Sell(-1, amount)
                } else {
                	hedgeMsg += ", hedging order amount is less than 0"
                }
            }
        }

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


Paramètres de stratégie:img

Adresse stratégique:https://www.fmz.com/strategy/265090

Opération stratégique:

img

img

La stratégie est un tutoriel, principalement utilisé pour l'étude, alors faites attention à l'utiliser dans un bot.


Plus de