The strategy that FMZ Quant brings to the table is the Deribit Options Delta Dynamic Hedging strategy, or DDH (dynamic delta hedging) strategy for short.
· Option pricing model, B-S model, option prices are determined based on [subject matter price], [power-wield price], [remaining time to expiration], [(implied) volatility], and [risk-free rate].
· Exposure of options:
-Delta – The directional risk of an option. If the Delta is +0.50, then the option, which performs profit or loss when the subject matter price goes up or down, can be considered as 0.50 spot. -Gamma – Acceleration of directional risk. For example, in the case of call options, due to the Gamma, the Delta will gradually move from +0.50 to +1.00 as the price continues to rise from the time the subject matter price is at the power-wield price. -Theta – Time Exposure. When you buy an option, if the subject matter price does not change, for each day that passes, you pay the fee shown in the Theta amount (Deribit is denominated in USD). When you sell an option, if the subject matter price does not change, you will receive the fee shown in the Theta amount every day. -Vega – Volatility Exposure. When you buy an option, Vega is positive, i.e. going long volatility. When the implied volatility increases, you gain on the Vega exposure. And vice versa, when you sell options, you gain as implied volatility decreases.
· DDH principle explanation Risk-neutral trading direction is achieved by matching the Delta of options and futures. Since the option’s Delta follows the change of the subject matter price, the Delta of futures and spots is unchanged. After taking a position in an option contract and balancing the Delta with futures hedging, the overall Delta will become unbalanced again as the subject matter price moves. Such a combination of options and futures positions requires continuous dynamic hedging to equalize Delta.
· For example: When we buy a call option, we hold a position in the long direction at this point. At this time, it is necessary to go short the futures to hedge the Delta of options, reaching the overall Delta neutral (0 or close to 0). Let’s ignore the remaining time to expiration of the option contract, volatility and other factors. Scenario 1: When the price of the subject matter rises, the Delta of the options increases, and the overall Delta moves to a positive number, and futures need to hedge again. Open some short positions to continue to go short the futures, so that the overall Delta is balanced again. (Before re-balancing, the Delta of options is large, while that of futures is relatively small. The marginal profit of call options exceeds the marginal loss of contract short positions, and the entire portfolio will yield.)
Scenario 2: As the price of the subject matter falls, the option portion of the Delta decreases and the overall Delta moves to a negative number, closing out a portion of the short futures position and bringing the overall Delta into balance again. (Before re-balancing, at this time, the Delta of options is small, while that of futures is relatively large. The marginal loss of call options is less than the marginal profit of contract short positions, and the entire portfolio will still yield gains.)
Therefore, in an ideal state, the increase and decrease of the subject matter will bring benefits as long as the market fluctuates.
However, there are also factors to consider: time value, transaction costs, and other factors.
Therefore, the explanation of hotshot on Zhihu is quoted:
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
· Aggregate market interface encapsulation, framework design · Strategy UI design · Strategic interaction design · Automatic hedge function design Source code:
// 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)
}
}
Strategy Address: https://www.fmz.com/strategy/265090
Strategy operation:
This strategy is a tutorial strategy, learning-oriented, please use it with caution in real bot.