Type/to search
8
Follow
1364
Followers
Pesquisa e exemplos sobre o desenho de estratégia de hedge de futuros de ordens pendentes
Discussions
Created 2021-11-26 14:15:31  Updated 2023-09-20 09:24:03
 3
 2446

img

Pesquisa e exemplos sobre o desenho de estratégia de hedge de futuros de ordens pendentes

O hedge futuro e à vista sempre foi projetado para detectar diferenças de preço e receber ordens de hedge quando a diferença de preço é atingida. Pode ser projetado como um hedge de ordem pendente? A resposta é sim. Hoje apresentarei aos leitores uma ideia de design e um protótipo de código para hedge de ordens pendentes.

Ideias de hedge de ordens pendentes

Quando há uma grande diferença nas ordens de compra e venda do mesmo ou do mesmo tipo de ativo subjacente em diferentes mercados, há uma oportunidade de hedge. Geralmente, aceitaremos as ordens de mercado que atendem à diferença de preço e manteremos uma posição de hedge. Portanto, os propósitos do hedging são duplos. O primeiro é proteger as posições de ordem, e o segundo é garantir que a diferença entre os preços de compra e venda atenda às nossas expectativas na maior extensão possível. A vantagem da negociação de ordens pendentes nesse sentido é que a taxa de transação é menor. A desvantagem é que não é fácil fechar um negócio e é fácil fechar o negócio de uma só vez.

Em seguida, projetamos a ideia de negociação para colocar uma ordem de compra na ordem de compra do livro de ordens do mercado A e colocar uma ordem de venda na ordem de venda do livro de ordens do mercado B. Em seguida, verificamos os pedidos pendentes da nossa conta e damos o próximo passo no processamento dos pedidos pendentes detectados. Por exemplo, quando uma alteração nas ordens pendentes é detectada, as posições de hedge futuras e à vista são imediatamente equilibradas, e as posições excedentes nas posições futuras e à vista são cobertas ou fechadas. De acordo com o aumento das posições protegidas, a distância entre a próxima ordem pendente e o primeiro nível do mercado é ajustada para proteger gradualmente e obter uma diferença de preço maior.

Lógica de hedge img

Design de código

Os comentários são escritos diretamente no código. Este exemplo é usado apenas para design de referência e foi testado apenas brevemente no disco de simulação OKEX V5. Este exemplo não é uma estratégia completa e serve apenas para referência.

javascript
// 临时参数 var fuContractType = "quarter" // 期货合约 var fuSymbol = "ETH_USDT" // 期货交易对 var spSymbol = "ETH_USDT" // 现货交易对 var minAmount = 0.1 // 每次交易量、最小交易量,币数 var step = 40 // 差价步长 var buff = 5 // 缓冲差价 var balanceType = "open" // 对于单腿成交平衡时, open 补仓 close 平仓 var depthManager = function(fuEx, spEx, fuCt, fuSymbol, spSymbol) { var self = {} self.fuExDepth = null self.spExDepth = null self.plusPrice = null self.minusPrice = null self.update = function() { spEx.SetCurrency(spSymbol) if (!IsVirtual()) { fuEx.SetCurrency(fuSymbol) } fuEx.SetContractType(fuCt) var fuRoutine = fuEx.Go("GetDepth") var spRoutine = spEx.Go("GetDepth") var fuDepth = fuRoutine.wait() var spDepth = spRoutine.wait() if (!fuDepth || !spDepth) { return false } self.fuExDepth = fuDepth self.spExDepth = spDepth if (fuDepth.Bids.length == 0 || fuDepth.Asks.length == 0 || spDepth.Bids.length == 0 || spDepth.Asks.length == 0) { return false } self.plusPrice = fuDepth.Bids[0].Price - spDepth.Asks[0].Price // futures Bid - spot Ask self.minusPrice = fuDepth.Asks[0].Price - spDepth.Bids[0].Price // futures Ask - spot Bid return true } self.getData = function() { return { "fuExDepth" : self.fuExDepth, "spExDepth" : self.spExDepth, "plusPrice" : self.plusPrice, "minusPrice" : self.minusPrice } } return self } var positionManager = function(fuEx, spEx, fuCt, fuSymbol, spSymbol, step, buffDiff, balanceType, initSpAcc) { var self = {} self.balanceType = balanceType self.depth = null self.level = 1 self.lastUpdateTs = 0 self.fuPos = [] self.spPos = [] self.initSpAcc = initSpAcc self.spAcc = null self.hedgePos = null self.hedgePosPrice = 0 self.minAmount = 0.01 self.offset = ["", 0] self.update = function() { spEx.SetCurrency(spSymbol) if (!IsVirtual()) { fuEx.SetCurrency(fuSymbol) } fuEx.SetContractType(fuCt) self.offset = ["", 0] var fuRoutine = fuEx.Go("GetPosition") var spRoutine = spEx.Go("GetAccount") var fuPos = fuRoutine.wait() var spAcc = spRoutine.wait() if (!fuPos || !spAcc) { return false } self.fuPos = fuPos self.spAcc = spAcc if (!self.initSpAcc) { return false } self.spPos = (spAcc.Stocks + spAcc.FrozenStocks) - (self.initSpAcc.Stocks + self.initSpAcc.FrozenStocks) // 当前减去最初,正数为做多 // 检测fuPos if (fuPos.length > 1) { return false } fuPosAmount = fuPos.length == 0 ? 0 : (fuPos[0].Type == PD_LONG ? fuPos[0].Amount : -fuPos[0].Amount) if ((fuPosAmount > 0 && self.spPos > 0) || (fuPosAmount < 0 && self.spPos < 0)) { return false } fuPosAmount = self.piece2Coin(fuPosAmount) self.hedgePos = (fuPosAmount == 0 || self.spPos == 0) ? 0 : (fuPosAmount < 0 && self.spPos > 0 ? Math.min(Math.abs(fuPosAmount), Math.abs(self.spPos)) : -Math.min(Math.abs(fuPosAmount), Math.abs(self.spPos))) var diffBalance = (spAcc.Balance + spAcc.FrozenBalance) - (self.initSpAcc.Balance + self.initSpAcc.FrozenBalance) if (self.hedgePos == 0) { self.hedgePosPrice = 0 } else { self.hedgePosPrice = fuPos[0].Price - (Math.abs(diffBalance) / Math.abs(self.spPos)) } self.offset[1] = fuPosAmount + self.spPos // 正数多头持仓溢出,负数空头持仓溢出 if (fuPosAmount > 0 && self.spPos < 0) { // 反套 self.offset[0] = "minus" } else if (fuPosAmount < 0 && self.spPos > 0) { self.offset[0] = "plus" } else if (fuPosAmount == 0 && self.spPos < 0) { self.offset[0] = "minus" } else if (fuPosAmount > 0 && self.spPos == 0) { self.offset[0] = "minus" } else if (fuPosAmount == 0 && self.spPos > 0) { self.offset[0] = "plus" } else if (fuPosAmount < 0 && self.spPos == 0) { self.offset[0] = "plus" } return true } self.getData = function() { return { "fuPos" : self.fuPos, "spPos" : self.spPos, "initSpAcc" : self.initSpAcc, "spAcc" : self.spAcc, "hedgePos" : self.hedgePos, "hedgePosPrice" : self.hedgePosPrice, } } self.keepBalance = function(depth) { var fuDepth = depth.fuExDepth var spDepth = depth.spExDepth if (self.offset[0] == "plus") { if (self.offset[1] >= self.minAmount) { if (self.balanceType == "close") { // 现货多头持仓多了,平现货多仓 spEx.Sell(-1, self.offset[1]) } else if (self.balanceType == "open") { // 现货多头持仓多了,开期货空头持仓 fuEx.SetDirection("sell") fuEx.Sell(-1, self.coin2Piece(Math.abs(self.offset[1]))) } } else if (self.offset[1] <= -self.minAmount) { if (self.balanceType == "close") { // 期货空头持仓多了,平期货空仓 fuEx.SetDirection("closesell") fuEx.Buy(-1, self.coin2Piece(Math.abs(self.offset[1]))) } else if (self.balanceType == "open") { // 期货空头持仓多了,开现货多头持仓 spEx.Buy(-1, spDepth.Asks[0].Price * Math.abs(self.offset[1])) } } return false } else if (self.offset[0] == "minus") { if (self.offset[1] >= self.minAmount) { if (self.balanceType == "close") { // 期货多头持仓多了,平期货多仓 fuEx.SetDirection("closebuy") fuEx.Sell(-1, self.coin2Piece(self.offset[1])) } else if (self.balanceType == "open") { // 期货多头持仓多了,开现货空头持仓 spEx.Sell(-1, self.offset[1]) } } else if (self.offset[1] <= -self.minAmount) { if (self.balanceType == "close") { // 现货空头持仓多了,平现货空仓 spEx.Buy(-1, spDepth.Asks[0].Price * Math.abs(self.offset[1])) } else if (self.balanceType == "open") { // 现货空头持仓多了,开期货多头持仓 fuEx.SetDirection("buy") fuEx.Buy(-1, self.coin2Piece(Math.abs(self.offset[1]))) } } return false } return true } self.process = function(depthManager) { var ts = new Date().getTime() var depth = depthManager.getData() var orders = self.getOrders() if (!orders) { return } self.depth = depth var fuOrders = orders[0] var spOrders = orders[1] if (fuOrders.length == 0 && spOrders.length == 0) { // 重置level if (self.hedgePos == 0) { self.level = 1 } else { self.level = Math.max(1, _N(self.hedgePos / self.minAmount, 0)) } // 限制最大持仓量 if (Math.abs(self.hedgePos) > 1) { return } // 挂单 var fuDepth = depth.fuExDepth var spDepth = depth.spExDepth self.update() if (self.hedgePos >= 0 && fuDepth.Bids[0].Price - spDepth.Asks[0].Price > 0) { // 正套 var distance = (step * self.level - (fuDepth.Asks[0].Price - spDepth.Bids[0].Price)) / 2 fuEx.SetDirection("sell") fuEx.Sell(fuDepth.Asks[0].Price + distance, self.coin2Piece(self.minAmount), fuDepth.Asks[0].Price, "挂单差价:", fuDepth.Asks[0].Price + distance - (spDepth.Bids[0].Price - distance)) spEx.Buy(spDepth.Bids[0].Price - distance, self.minAmount, spDepth.Bids[0].Price) } else if (self.hedgePos <= 0 && spDepth.Bids[0].Price - fuDepth.Asks[0].Price > 0) { // 反套 var distance = (step * self.level - (spDepth.Asks[0].Price - fuDepth.Bids[0].Price)) / 2 fuEx.SetDirection("buy") fuEx.Buy(fuDepth.Bids[0].Price - distance, self.coin2Piece(self.minAmount), fuDepth.Bids[0].Price, "挂单差价:", spDepth.Asks[0].Price + distance - (fuDepth.Bids[0].Price - distance)) spEx.Sell(spDepth.Asks[0].Price + distance, self.minAmount, spDepth.Asks[0].Price) } } else if (fuOrders.length == 1 && spOrders.length == 1) { var fuDepth = depth.fuExDepth var spDepth = depth.spExDepth // 判断位置 var isCancelAll = false if (self.hedgePos >= 0 && fuDepth.Bids[0].Price - spDepth.Asks[0].Price > 0) { // 正套 var distance = (step * self.level - (fuDepth.Asks[0].Price - spDepth.Bids[0].Price)) / 2 if (Math.abs(fuOrders[0].Price - (fuDepth.Asks[0].Price + distance)) > buffDiff || Math.abs(spOrders[0].Price - (spDepth.Bids[0].Price - distance)) > buffDiff) { isCancelAll = true } } else if (self.hedgePos <= 0 && spDepth.Bids[0].Price - fuDepth.Asks[0].Price > 0) { // 反套 var distance = (step * self.level - (spDepth.Asks[0].Price - fuDepth.Bids[0].Price)) / 2 if (Math.abs(spOrders[0].Price - (spDepth.Asks[0].Price + distance)) > buffDiff || Math.abs(fuOrders[0].Price - (fuDepth.Bids[0].Price - distance)) > buffDiff) { isCancelAll = true } } else { isCancelAll = true } if (isCancelAll) { self.cancelAll(fuEx, fuOrders) self.cancelAll(spEx, spOrders) self.lastUpdateTs = 0 } } else { self.cancelAll(fuEx, fuOrders) self.cancelAll(spEx, spOrders) self.lastUpdateTs = 0 } if (ts - self.lastUpdateTs > 1000 * 60 * 2) { self.update() self.keepBalance(depth) self.update() self.lastUpdateTs = ts } LogStatus(_D()) // 状态栏可以设计输出需要观察的数据、信息 } self.getOrders = function() { spEx.SetCurrency(spSymbol) if (!IsVirtual()) { fuEx.SetCurrency(fuSymbol) } fuEx.SetContractType(fuCt) var fuRoutine = fuEx.Go("GetOrders") var spRoutine = spEx.Go("GetOrders") var fuOrders = fuRoutine.wait() var spOrders = spRoutine.wait() if (!fuOrders || !spOrders) { return false } return [fuOrders, spOrders] } // 币转合约张数 self.coin2Piece = function(amount) { if (IsVirtual()) { if (fuEx.GetName() == "Futures_Binance") { return amount } else if (fuEx.GetName() == "Futures_OKCoin") { var price = (self.depth.fuExDepth.Bids[0].Price + self.depth.fuExDepth.Asks[0].Price) / 2 return _N(amount / (100 / price), 0) } else { throw "not support" } } if (fuEx.GetName() == "Futures_OKCoin") { if (fuEx.GetQuoteCurrency() == "USDT") { return _N(amount * 10, 0) } else if (fuEx.GetQuoteCurrency() == "USD") { var price = (self.depth.fuExDepth.Bids[0].Price + self.depth.fuExDepth.Asks[0].Price) / 2 return _N(amount / (100 / price), 0) } else { throw "not support" } } else { throw "not support" } } // 合约张数转币 self.piece2Coin = function(amount) { if (IsVirtual()) { if (fuEx.GetName() == "Futures_Binance") { return amount } else if (fuEx.GetName() == "Futures_OKCoin") { var price = (self.depth.fuExDepth.Bids[0].Price + self.depth.fuExDepth.Asks[0].Price) / 2 return amount * 100 / price } else { throw "not support" } } if (fuEx.GetName() == "Futures_OKCoin") { if (fuEx.GetQuoteCurrency() == "USDT") { return amount * 0.1 } else if (fuEx.GetQuoteCurrency() == "USD") { var price = (self.depth.fuExDepth.Bids[0].Price + self.depth.fuExDepth.Asks[0].Price) / 2 return amount * 100 / price } else { throw "not support" } } else { throw "not support" } } self.cancelAll = function(e, orders) { var isFirst = true while (true) { Sleep(500) if (orders && isFirst) { isFirst = false } else { orders = e.GetOrders() } if (!orders) { continue } else { for (var i = 0 ; i < orders.length ; i++) { e.CancelOrder(orders[i].Id, orders[i]) } } if (orders.length == 0) { break } } } self.CoverAll = function() { // 全部平仓 // 这里可以实现一个,一键平仓的功能 } self.setMinAmount = function(minAmount) { self.minAmount = minAmount } self.init = function() { while(!self.spAcc) { self.update() Sleep(1000) } if (!self.initSpAcc) { var positionManager_initSpAcc = _G("positionManager_initSpAcc") if (!positionManager_initSpAcc) { self.initSpAcc = self.spAcc _G("positionManager_initSpAcc", self.initSpAcc) } else { self.initSpAcc = positionManager_initSpAcc } } else { _G("positionManager_initSpAcc", self.initSpAcc) } // 打印初始信息 Log("self.initSpAcc:", self.initSpAcc.Balance, self.initSpAcc.FrozenBalance, self.initSpAcc.Stocks, self.initSpAcc.FrozenStocks) } self.init() return self } function main() { _G(null) // 清空持久化数据 LogReset(1) // 重置日志 // 以下代码可以切换OKEX模拟盘 // exchanges[0].IO("simulate", true) // exchanges[1].IO("simulate", true) var dm = depthManager(exchanges[0], exchanges[1], fuContractType, fuSymbol, spSymbol) var pm = positionManager(exchanges[0], exchanges[1], fuContractType, fuSymbol, spSymbol, step, buff, balanceType) pm.setMinAmount(minAmount) while (true) { if (!dm.update()) { Sleep(3000) continue } var cmd = GetCommand() if (cmd) { // 处理交互 Log("交互命令:", cmd) var arr = cmd.split(":") if (arr[0] == "") { pm.CoverAll() } } pm.process(dm) Sleep(5000) } }

Análise de backtest

img

img

É possível observar que os pedidos são feitos e cancelados com muita frequência. A julgar pelos lucros calculados pelo sistema de backtesting, a conta da bolsa de futuros perdeu -0,01666 ETH, e a bolsa à vista obteve um lucro de 842,23758 USDT. No final do backtest, o preço à vista do ETH era 4252USDT.-0.01666 * 4252 = -70.83832000000001. Combinado com os lucros à vista, a situação geral é lucrativa.

No entanto, isso é apenas um backtest, e questões mais detalhadas devem ser resolvidas na negociação real.

Related Recommendations
Comment
All comments (3)

    梦总,支持okex统一账户模式不 现货买入ETH 期货直接当保证金做空

    4 years ago

    您好, 支持OKEX V5接口的,支持这个模式。

    4 years ago

    4 years ago
  • 1
iPhone Download
Forums
PINE Language
© 2015 - ∞ INVENTOR PTE LTD (SG)