
Lindung nilai berjangka dan spot selalu dirancang untuk mendeteksi perbedaan harga dan menerima perintah lindung nilai ketika perbedaan harga terpenuhi. Bisakah ini dirancang sebagai lindung nilai atas pesanan tertunda? Jawabannya iya. Hari ini saya akan memperkenalkan kepada pembaca ide desain dan prototipe kode untuk lindung nilai pesanan tertunda.
Jika terdapat perbedaan besar antara pesanan beli dan jual atas aset dasar yang sama atau jenis yang sama di pasar yang berbeda, terdapat peluang untuk melakukan lindung nilai. Secara umum, kami akan mengambil pesanan pasar yang memenuhi perbedaan harga dan menahan posisi lindung nilai. Oleh karena itu, tujuan lindung nilai ada dua. Yang pertama adalah untuk melindungi posisi pesanan, dan yang kedua adalah untuk memastikan bahwa perbedaan antara harga beli dan harga jual memenuhi harapan kita semaksimal mungkin. Keuntungan perdagangan pending order dalam hal ini adalah tingkat biaya transaksinya lebih rendah. Kerugiannya adalah tidak mudah untuk menutup transaksi, dan mudah untuk menutup transaksi dengan satu langkah.
Kemudian kami merancang ide perdagangan untuk menempatkan pesanan beli pada pesanan beli buku pesanan pasar A, dan menempatkan pesanan jual pada pesanan jual buku pesanan pasar B. Lalu kami memeriksa pesanan tertunda di akun kami dan mengambil langkah berikutnya dalam memproses pesanan tertunda yang terdeteksi. Misalnya, ketika perubahan pada pending order terdeteksi, posisi lindung nilai berjangka dan spot segera diseimbangkan, dan posisi yang meluap pada posisi berjangka dan spot ditutup atau ditutup. Menurut peningkatan posisi lindung nilai, jarak antara pending order berikutnya dan tingkatan pertama pasar disesuaikan untuk secara bertahap melakukan lindung nilai dan memperoleh perbedaan harga yang lebih besar.
Logika lindung nilai 
Komentar ditulis langsung dalam kode. Contoh ini hanya digunakan untuk desain referensi dan hanya diuji secara singkat pada disk simulasi OKEX V5. Contoh ini bukanlah strategi lengkap dan hanya untuk referensi.
// 临时参数
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)
}
}


Dapat dilihat bahwa pesanan ditempatkan dan dibatalkan sangat sering. Dilihat dari keuntungan yang dihitung oleh sistem pengujian ulang, akun bursa berjangka kehilangan -0,01666 ETH, dan bursa spot mendapat keuntungan sebesar 842,23758 USDT. Pada akhir pengujian ulang, harga spot ETH adalah 4252USDT.-0.01666 * 4252 = -70.83832000000001. Dikombinasikan dengan laba spot, situasi keseluruhannya menguntungkan.
Akan tetapi, ini hanyalah uji ulang, dan rincian lebih lanjut harus dipecahkan dalam perdagangan sesungguhnya.