
Bagi pemula dalam reka bentuk strategi, strategi lindung nilai ialah strategi latihan yang sangat baik. Artikel ini melaksanakan strategi lindung nilai tempat mata wang digital yang mudah tetapi dunia sebenar, dengan harapan bahawa pemula dapat mempelajari beberapa pengalaman reka bentuk.
Pertama sekali, jelas bahawa strategi yang akan direka adalah strategi lindung nilai mata wang digital Kami mereka strategi lindung nilai yang paling mudah, iaitu menjual di bursa dengan harga yang lebih tinggi antara dua bursa spot dan membeli di bursa dengan harga yang lebih rendah. untuk membuat keuntungan. Apabila semua bursa dengan harga yang lebih tinggi didenominasikan dalam syiling (kerana semua syiling dengan harga yang lebih tinggi dijual), dan semua bursa dengan harga yang lebih rendah adalah dalam syiling (semua syiling dengan harga yang lebih rendah dibeli), adalah mustahil untuk Hedge. Pada masa ini, anda hanya boleh menunggu harga berbalik dan melindung nilai.
Apabila melindung nilai, pertukaran mempunyai sekatan ketepatan pada harga dan kuantiti pesanan, dan terdapat juga had kuantiti pesanan minimum. Sebagai tambahan kepada had minimum, strategi juga mesti mempertimbangkan volum pesanan maksimum untuk lindung nilai pada satu masa Jika volum pesanan terlalu besar, pesanan tidak akan mencukupi di pasaran. Anda juga perlu mempertimbangkan cara menukar menggunakan kadar pertukaran jika kedua-dua bursa mempunyai mata wang denominasi yang berbeza. Apabila melindung nilai, yuran pengendalian dan kegelinciran pesanan adalah kedua-duanya kos transaksi Tidak boleh dilindung nilai selagi terdapat perbezaan harga Oleh itu, terdapat juga nilai pencetus untuk melindung nilai perbezaan harga tahap tertentu, lindung nilai akan mengakibatkan kerugian.
Berdasarkan pertimbangan ini, strategi perlu mereka bentuk beberapa parameter:
hedgeDiffPrice, apabila perbezaan harga melebihi nilai ini, operasi lindung nilai dicetuskan.minHedgeAmount, kuantiti pesanan minimum (dalam syiling) yang boleh dilindung nilai.maxHedgeAmount, kuantiti pesanan maksimum (bilangan syiling) untuk satu lindung nilai.pricePrecisionA, ketepatan harga pesanan (bilangan tempat perpuluhan) pertukaran A.amountPrecisionA, ketepatan kuantiti pesanan (bilangan tempat perpuluhan) pertukaran A.pricePrecisionB, ketepatan harga pesanan (bilangan tempat perpuluhan) pertukaran B.amountPrecisionB, ketepatan kuantiti pesanan (bilangan tempat perpuluhan) pertukaran B.rateA, penukaran kadar pertukaran objek pertukaran yang pertama ditambah, nilai lalai ialah 1, tiada penukaran.rateB, penukaran kadar pertukaran objek pertukaran tambahan kedua, nilai lalai ialah 1 dan tiada penukaran dilakukan.Strategi lindung nilai perlu memastikan bilangan syiling dalam dua akaun tidak berubah (iaitu, tidak memegang sebarang kedudukan berarah dan mengekalkan neutraliti), jadi perlu ada logik imbangan dalam strategi untuk sentiasa menyemak baki. Semasa menyemak baki, tidak dapat dielakkan untuk mendapatkan data aset daripada dua bursa. Kita perlu menulis fungsi untuk menggunakannya.
function updateAccs(arrEx) {
var ret = []
for (var i = 0 ; i < arrEx.length ; i++) {
var acc = arrEx[i].GetAccount()
if (!acc) {
return null
}
ret.push(acc)
}
return ret
}
Jika pesanan tidak dilaksanakan selepas meletakkannya, kami perlu membatalkannya tepat pada masanya dan tidak membiarkannya belum selesai. Operasi ini perlu diproses dalam kedua-dua modul baki dan dalam logik lindung nilai, jadi fungsi pengeluaran pesanan penuh perlu direka bentuk.
function cancelAll() {
_.each(exchanges, function(ex) {
while (true) {
var orders = _C(ex.GetOrders)
if (orders.length == 0) {
break
}
for (var i = 0 ; i < orders.length ; i++) {
ex.CancelOrder(orders[i].Id, orders[i])
Sleep(500)
}
}
})
}
Apabila mengimbangi bilangan syiling, kita perlu mencari harga sejumlah syiling yang terkumpul dalam kedalaman data tertentu, jadi kita memerlukan fungsi sedemikian untuk mengendalikannya.
function getDepthPrice(depth, side, amount) {
var arr = depth[side]
var sum = 0
var price = null
for (var i = 0 ; i < arr.length ; i++) {
var ele = arr[i]
sum += ele.Amount
if (sum >= amount) {
price = ele.Price
break
}
}
return price
}
Kemudian kita perlu mereka bentuk dan menulis operasi perintah lindung nilai khusus, yang perlu direka bentuk untuk menjadi pesanan serentak:
function hedge(buyEx, sellEx, price, amount) {
var buyRoutine = buyEx.Go("Buy", price, amount)
var sellRoutine = sellEx.Go("Sell", price, amount)
Sleep(500)
buyRoutine.wait()
sellRoutine.wait()
}
Akhir sekali, mari kita lengkapkan reka bentuk fungsi keseimbangan, yang agak rumit.
keepBalance
function keepBalance(initAccs, nowAccs, depths) {
var initSumStocks = 0
var nowSumStocks = 0
_.each(initAccs, function(acc) {
initSumStocks += acc.Stocks + acc.FrozenStocks
})
_.each(nowAccs, function(acc) {
nowSumStocks += acc.Stocks + acc.FrozenStocks
})
var diff = nowSumStocks - initSumStocks
// 计算币差
if (Math.abs(diff) > minHedgeAmount && initAccs.length == nowAccs.length && nowAccs.length == depths.length) {
var index = -1
var available = []
var side = diff > 0 ? "Bids" : "Asks"
for (var i = 0 ; i < nowAccs.length ; i++) {
var price = getDepthPrice(depths[i], side, Math.abs(diff))
if (side == "Bids" && nowAccs[i].Stocks > Math.abs(diff)) {
available.push(i)
} else if (price && nowAccs[i].Balance / price > Math.abs(diff)) {
available.push(i)
}
}
for (var i = 0 ; i < available.length ; i++) {
if (index == -1) {
index = available[i]
} else {
var priceIndex = getDepthPrice(depths[index], side, Math.abs(diff))
var priceI = getDepthPrice(depths[available[i]], side, Math.abs(diff))
if (side == "Bids" && priceIndex && priceI && priceI > priceIndex) {
index = available[i]
} else if (priceIndex && priceI && priceI < priceIndex) {
index = available[i]
}
}
}
if (index == -1) {
Log("无法平衡")
} else {
// 平衡下单
var price = getDepthPrice(depths[index], side, Math.abs(diff))
if (price) {
var tradeFunc = side == "Bids" ? exchanges[index].Sell : exchanges[index].Buy
tradeFunc(price, Math.abs(diff))
} else {
Log("价格无效", price)
}
}
return false
} else if (!(initAccs.length == nowAccs.length && nowAccs.length == depths.length)) {
Log("错误:", "initAccs.length:", initAccs.length, "nowAccs.length:", nowAccs.length, "depths.length:", depths.length)
return true
} else {
return true
}
}
Sekarang fungsi ini telah direka bentuk mengikut keperluan strategi, kita boleh mula mereka bentuk fungsi utama strategi.
Di FMZ strateginya adalah untukmainFungsi mula dilaksanakan. wujudmainPada permulaan fungsi kita perlu melakukan beberapa permulaan strategi.
var exA = exchanges[0]
var exB = exchanges[1]
Ini menjadikannya sangat selesa untuk menulis kod kemudian.
// 精度,汇率设置
if (rateA != 1) {
// 设置汇率A
exA.SetRate(rateA)
Log("交易所A设置汇率:", rateA, "#FF0000")
}
if (rateB != 1) {
// 设置汇率B
exB.SetRate(rateB)
Log("交易所B设置汇率:", rateB, "#FF0000")
}
exA.SetPrecision(pricePrecisionA, amountPrecisionA)
exB.SetPrecision(pricePrecisionB, amountPrecisionB)
Jika parameter kadar pertukaranrateA、rateBAda yang ditetapkan kepada 1 (lalai ialah 1), iaiturateA != 1ataurateB != 1Tidak akan mencetuskan, jadi tiada penukaran kadar pertukaran akan ditetapkan.

Kadangkala apabila dasar dimulakan, semua log dan data yang direkodkan perlu dipadamkan. Anda boleh mereka bentuk parameter antara muka strategiisReset, dan kemudian reka kod tetapan semula dalam bahagian permulaan strategi, contohnya:
if (isReset) { // 当isReset为真时重置数据
_G(null)
LogReset(1)
LogProfitReset()
LogVacuum()
Log("重置所有数据", "#FF0000")
}
nowAccsPembolehubah ini digunakan untuk merekod data akaun semasa, menggunakan fungsi yang baru kami reka.updateAccsDapatkan data akaun pertukaran semasa.initAccsDigunakan untuk merekodkan status akaun awal (data seperti bilangan syiling dan bilangan syiling denominasi Exchange A dan Exchange B). untukinitAccsPenggunaan pertama_G()Pemulihan fungsi (_Fungsi G akan merakam data secara berterusan dan boleh mengembalikan data yang direkodkan semula untuk mendapatkan butiran, lihat dokumentasi API:Pautan), jika pertanyaan gagal, gunakan maklumat akaun semasa untuk menetapkan nilai dan penggunaan_GRekod fungsi.Sebagai contoh, kod berikut:
var nowAccs = _C(updateAccs, exchanges)
var initAccs = _G("initAccs")
if (!initAccs) {
initAccs = nowAccs
_G("initAccs", initAccs)
}
Kod dalam gelung utama ialah proses setiap pusingan pelaksanaan logik strategi Pelaksanaan salingan berterusan membentuk gelung utama strategi. Mari kita lihat aliran setiap pelaksanaan program dalam gelung utama.
var ts = new Date().getTime()
var depthARoutine = exA.Go("GetDepth")
var depthBRoutine = exB.Go("GetDepth")
var depthA = depthARoutine.wait()
var depthB = depthBRoutine.wait()
if (!depthA || !depthB || depthA.Asks.length == 0 || depthA.Bids.length == 0 || depthB.Asks.length == 0 || depthB.Bids.length == 0) {
Sleep(500)
continue
}
Di sini anda boleh melihat fungsi serentak platform FMZ.exchange.Go, mencipta panggilanGetDepth()Objek konkurensi antara mukadepthARoutine、depthBRoutine. Apabila kedua-dua objek serentak ini dicipta, panggilGetDepth()Antara muka juga berlaku serta-merta, dan dua permintaan untuk mendapatkan data kedalaman telah dihantar ke bursa.
Kemudian telefondepthARoutine、depthBRoutineObjekwait()Kaedah untuk mendapatkan data kedalaman.
Selepas mendapatkan data kedalaman, perlu menyemak data kedalaman untuk menentukan kesahihannya. Pencetus pelaksanaan untuk anomali datacontinuePernyataan melaksanakan semula gelung utama.
价差值Parameter atau差价比例parameter? var targetDiffPrice = hedgeDiffPrice
if (diffAsPercentage) {
targetDiffPrice = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentage
}
Kami membuat reka bentuk sedemikian dari segi parameter. Parameter FMZ boleh berdasarkan parameter tertentutunjukataubersembunyi, jadi kita boleh membuat parameter untuk memutuskan sama ada hendak digunakan价格差, masih差价比例。

Parameter telah ditambahkan pada parameter antara muka strategidiffAsPercentage. Dua parameter lain yang ditunjukkan atau disembunyikan berdasarkan parameter ini ditetapkan kepada:
hedgeDiffPrice@!diffAsPercentage,biladiffAsPercentageFalse memaparkan parameter ini.
hedgeDiffPercentage@diffAsPercentage,biladiffAsPercentageBenar untuk memaparkan parameter ini.
Selepas reka bentuk ini, kami menyemakdiffAsPercentageParameter adalah berdasarkan nisbah perbezaan harga sebagai syarat pencetus lindung nilai. NyahtandadiffAsPercentageParameternya ialah menggunakan perbezaan harga sebagai syarat pencetus lindung nilai.
if (depthA.Bids[0].Price - depthB.Asks[0].Price > targetDiffPrice && Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount) >= minHedgeAmount) { // A -> B 盘口条件满足
var price = (depthA.Bids[0].Price + depthB.Asks[0].Price) / 2
var amount = Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount)
if (nowAccs[0].Stocks > minHedgeAmount && nowAccs[1].Balance / price > minHedgeAmount) {
amount = Math.min(amount, nowAccs[0].Stocks, nowAccs[1].Balance / price, maxHedgeAmount)
Log("触发A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, price, amount, nowAccs[1].Balance / price, nowAccs[0].Stocks) // 提示信息
hedge(exB, exA, price, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
} else if (depthB.Bids[0].Price - depthA.Asks[0].Price > targetDiffPrice && Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount) >= minHedgeAmount) { // B -> A 盘口条件满足
var price = (depthB.Bids[0].Price + depthA.Asks[0].Price) / 2
var amount = Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount)
if (nowAccs[1].Stocks > minHedgeAmount && nowAccs[0].Balance / price > minHedgeAmount) {
amount = Math.min(amount, nowAccs[1].Stocks, nowAccs[0].Balance / price, maxHedgeAmount)
Log("触发B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, price, amount, nowAccs[0].Balance / price, nowAccs[1].Stocks) // 提示信息
hedge(exA, exB, price, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
}
Terdapat beberapa syarat pencetus lindung nilai:
1. Pertama, memenuhi perbezaan harga lindung nilai. Lindung nilai hanya boleh dilakukan apabila perbezaan harga pasaran memenuhi parameter perbezaan harga yang ditetapkan.
2. Volum lindung nilai di pasaran mesti memenuhi volum lindung nilai minimum yang ditetapkan dalam parameter Kerana pertukaran yang berbeza mungkin mempunyai volum pesanan minimum yang berbeza, yang terkecil daripada kedua-duanya harus diambil.
3. Terdapat aset yang mencukupi dalam pertukaran untuk operasi penjualan untuk dijual, dan terdapat aset yang mencukupi dalam pertukaran untuk operasi belian untuk dibeli.
Apabila syarat ini dipenuhi, fungsi lindung nilai dilaksanakan untuk membuat perintah lindung nilai. Sebelum fungsi utama kami mengisytiharkan pembolehubah terlebih dahuluisTradeDigunakan untuk menandakan sama ada lindung nilai berlaku Jika lindung nilai dicetuskan, pembolehubah ini ditetapkan kepadatrue. Dan tetapkan semula pembolehubah globallastKeepBalanceTSTetapkan lastKeepBalanceTS kepada 0 (lastKeepBalanceTS digunakan untuk menandakan cap masa bagi operasi pengimbangan terbaharu. Menetapkannya kepada 0 akan mencetuskan operasi pengimbangan serta-merta), dan kemudian membatalkan semua pesanan yang belum selesai.
if (ts - lastKeepBalanceTS > keepBalanceCyc * 1000) {
nowAccs = _C(updateAccs, exchanges)
var isBalance = keepBalance(initAccs, nowAccs, [depthA, depthB])
cancelAll()
if (isBalance) {
lastKeepBalanceTS = ts
if (isTrade) {
var nowBalance = _.reduce(nowAccs, function(sumBalance, acc) {return sumBalance + acc.Balance}, 0)
var initBalance = _.reduce(initAccs, function(sumBalance, acc) {return sumBalance + acc.Balance}, 0)
LogProfit(nowBalance - initBalance, nowBalance, initBalance, nowAccs)
isTrade = false
}
}
}
Anda boleh melihat bahawa fungsi imbangan dilaksanakan dengan kerap, tetapi jika operasi lindung nilai dicetuskan,lastKeepBalanceTSJika ditetapkan semula kepada 0, operasi baki akan dicetuskan serta-merta. Selepas baki berjaya, keuntungan akan dikira.
LogStatus(_D(), "A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, " B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, " targetDiffPrice:", targetDiffPrice, "\n",
"当前A,Stocks:", nowAccs[0].Stocks, "FrozenStocks:", nowAccs[0].FrozenStocks, "Balance:", nowAccs[0].Balance, "FrozenBalance", nowAccs[0].FrozenBalance, "\n",
"当前B,Stocks:", nowAccs[1].Stocks, "FrozenStocks:", nowAccs[1].FrozenStocks, "Balance:", nowAccs[1].Balance, "FrozenBalance", nowAccs[1].FrozenBalance, "\n",
"初始A,Stocks:", initAccs[0].Stocks, "FrozenStocks:", initAccs[0].FrozenStocks, "Balance:", initAccs[0].Balance, "FrozenBalance", initAccs[0].FrozenBalance, "\n",
"初始B,Stocks:", initAccs[1].Stocks, "FrozenStocks:", initAccs[1].FrozenStocks, "Balance:", initAccs[1].Balance, "FrozenBalance", initAccs[1].FrozenBalance)
Bar status tidak begitu rumit dalam reka bentuk Ia memaparkan masa semasa, perbezaan harga dari pertukaran A ke pertukaran B, dan perbezaan harga dari pertukaran B ke pertukaran A. Memaparkan sebaran sasaran lindung nilai semasa. Memaparkan data aset akaun pertukaran A dan akaun pertukaran B.
Dari segi parameter, kami mereka bentuk parameter nilai kadar penukaran pada permulaan strategimainKami juga mereka bentuk penukaran kadar pertukaran untuk operasi awal fungsi. Perlu diingatkan bahawaSetRateFungsi penukaran kadar pertukaran perlu dilaksanakan terlebih dahulu.
Kerana fungsi ini mempengaruhi dua peringkat:
BTC_USDT, unit harga adalahUSDT, mata wang yang tersedia dalam aset akaun jugaUSDT. Jika saya ingin menukarnya kepada CNY, tetapkannya dalam kodexchange.SetRate(6.8)CumaexchangeData yang diperolehi oleh semua fungsi di bawah objek pertukaran ini ditukar kepada CNY.
Mengapakah mata wang digunakan untuk penukaran?SetRateFungsi lulusKadar pertukaran daripada mata wang semasa kepada mata wang sasaran。Strategi lengkap:Strategi lindung nilai spot untuk mata wang yang berbeza (tutorial)