本次FMZ量化带来的策略是Deribit期权Delta动态对冲策略,简称DDH(dynamic delta hedging)策略。
情况2:
标的物价格下跌,期权部分的Delta减小,总体Delta向负数移动,平仓一部分空头的期货持仓,使总体Delta再次平衡。
(再次平衡前,此时期权的Delta小,期货的Delta相对大,看涨期权的边际损失小于合约空头的边际盈利,整个组合还是会出现收益)
所以理想状态,标的物涨跌都会带来收益,只要市场有波动即可。
然而还需要考虑的因素还有:时间价值、交易成本等因素。
所以引用知乎大牛的解释:
Gamma Scalping 的关注点并不是delta,dynamic delta hedging 只是过程中规避underlying价格风险的一种做法而已。
Gamma Scalping 关注的是Alpha,此Alpha不是选股的Alpha,这里的Alpha = Gamma/Theta也就是单位Theta的时间损耗换来多少Gamma,
这个是关注的点。可以构建出上涨和下跌都浮盈的组合,但一定伴随时间损耗,那问题就在于性价比了。
作者:许哲
链接:https://www.zhihu.com/question/51630805/answer/128096385
源码:
// 构造函数
function createManager(e, subscribeList, msg) {
var self = {}
self.supportList = [\"Futures_Binance\", \"Huobi\", \"Futures_Deribit\"] // 支持的交易所的
// 对象属性
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 = [] // 接口获取的所有行情数据,定义数据格式:{bid1: 123, ask1: 123, symbol: \"xxx\"}}
self.subscribeTickers = [] // 需要的行情数据,定义数据格式:{bid1: 123, ask1: 123, symbol: \"xxx\"}}
self.accData = null
self.pos = null
// 初始化函数
self.init = function() {
// 判断是否支持该交易所
if (!_.contains(self.supportList, self.name)) {
throw \"not support\"
}
}
self.setBase = function(base) {
// 切换基地址,用于切换为模拟盘
self.e.SetBase(base)
Log(self.name, self.label, \"切换为模拟盘:\", base)
}
// 判断数据精度
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
}
// 更新资产
self.updateAcc = function(callBackFuncGetAcc) {
var ret = callBackFuncGetAcc(self)
if (!ret) {
return false
}
self.accData = ret
return true
}
// 更新持仓
self.updatePos = function(httpMethod, url, params) {
var pos = self.e.IO(\"api\", httpMethod, url, params)
var ret = []
if (!pos) {
return false
} else {
// 整理数据
// {\"jsonrpc\":\"2.0\",\"result\":[],\"usIn\":1616484238870404,\"usOut\":1616484238870970,\"usDiff\":566,\"testnet\":true}
try {
_.each(pos.result, function(ele) {
ret.push(ele)
})
} catch(err) {
Log(\"错误:\", err)
return false
}
self.pos = ret
}
return true
}
// 更新行情数据
self.updateTicker = function(url, callBackFuncGetArr, callBackFuncGetTicker) {
var tickers = []
var subscribeTickers = []
var ret = self.httpQuery(url)
if (!ret) {
return false
}
// Log(\"测试\", ret)// 测试
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(\"错误:\", 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(\"错误:\", 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
}
// 返回持仓表格
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 : []
}
/* 接口返回的持仓数据格式
{
\"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])
}
// 遍历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
}
// 初始化
self.init()
return self
}
function main() {
// 初始化,清空日志
if(isResetLog) {
LogReset(1)
}
var m1 = createManager(exchanges[0], [], \"option\")
var m2 = createManager(exchanges[1], [\"BTC-PERPETUAL\"], \"future\")
// 切换为模拟盘
var base = \"https://www.deribit.com\"
if (isTestNet) {
m1.setBase(testNetBase)
m2.setBase(testNetBase)
base = testNetBase
}
while(true) {
// 期权
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}})
// 永续期货
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
}
// 更新持仓
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
}
// 交互
var cmd = GetCommand()
if(cmd) {
// 处理交互
Log(\"交互命令:\", cmd)
var arr = cmd.split(\":\")
// cmdClearLog
if(arr[0] == \"setContractType\") {
// parseFloat(arr[1])
m1.e.SetContractType(arr[1])
Log(\"exchanges[0]交易所对象设置合约:\", 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(\"执行价格:\", price, \"执行数量:\", amount, \"执行方向:\", 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(\"执行价格:\", price, \"执行数量:\", amount, \"执行方向:\", arr[0])
} else if (arr[0] == \"setHedgeDeltaStep\") {
hedgeDeltaStep = parseFloat(arr[1])
Log(\"设置参数hedgeDeltaStep:\", hedgeDeltaStep)
}
}
// 获取期货合约价格
var perpetualTicker = m2.getTicker(\"BTC-PERPETUAL\")
var hedgeMsg = \" PERPETUAL:\" + JSON.stringify(perpetualTicker)
// 从账户数据中获取delta总值
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 大于0 对冲期货做空
var amount = _N(Math.abs(sumDelta) * perpetualTicker.ask1, -1)
if (amount > 10) {
Log(\"超过对冲阈值,当前总delta:\", sumDelta, \"买入期货\")
m2.e.SetContractType(\"BTC-PERPETUAL\")
m2.e.SetDirection(\"buy\")
m2.e.Buy(-1, amount)
} else {
hedgeMsg += \", 对冲下单量小于10\"
}
} else {
// delta 小于0 对冲期货做多
var amount = _N(Math.abs(sumDelta) * perpetualTicker.bid1, -1)
if (amount > 10) {
Log(\"超过对冲阈值,当前总delta:\", sumDelta, \"卖出期货\")
m2.e.SetContractType(\"BTC-PERPETUAL\")
m2.e.SetDirection(\"sell\")
m2.e.Sell(-1, amount)
} else {
hedgeMsg += \", 对冲下单量小于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

![]()

![]()
本策略为教学策略,学习为主,实盘请谨慎使用。
买好币上币库:https://www.kucoin.com/r/1f7w3