Strategi lindung nilai adalah strategi amalan yang sangat baik untuk pemula dalam reka bentuk strategi.
Pertama sekali, adalah jelas bahawa strategi yang akan direka adalah strategi lindung nilai mata wang kripto spot. Kami merancang strategi lindung nilai yang paling mudah. Kami menjual di bursa dengan harga yang lebih tinggi hanya antara dua bursa spot, dan membeli di bursa dengan harga yang lebih rendah untuk mengambil perbezaannya. Apabila bursa dengan harga yang lebih tinggi adalah semua duit syiling berdenominasi (kerana duit syiling dengan harga yang lebih tinggi dijual), dan bursa dengan harga yang lebih rendah adalah semua duit syiling (uang syiling dengan harga yang lebih rendah dibeli), ia tidak boleh dilindung nilai. Pada masa ini, kita hanya boleh menunggu pembalikan harga untuk lindung nilai.
Apabila lindung nilai, harga dan kuantiti pesanan adalah terhad oleh bursa, dan terdapat juga had pada kuantiti pesanan minimum. Selain had minimum, strategi dalam lindung nilai juga perlu mempertimbangkan jumlah pesanan maksimum pada satu masa. Jika jumlah pesanan terlalu besar, tidak akan ada jumlah pesanan yang mencukupi. Ia juga perlu mempertimbangkan bagaimana menukar kadar pertukaran jika dua duit syiling yang berdenominasi pertukaran berbeza. Semasa lindung nilai, yuran pengendalian dan slippage pemegang pesanan adalah semua kos transaksi, tidak selagi terdapat perbezaan harga boleh dilindung nilai. Oleh itu, perbezaan harga lindung nilai juga mempunyai nilai pemicu. Jika lebih rendah daripada perbezaan harga tertentu, lindung nilai akan hilang.
Berdasarkan pertimbangan ini, strategi perlu direka dengan beberapa parameter:
hedgeDiffPrice
, apabila perbezaan melebihi nilai ini, operasi lindung nilai dipicu.minHedgeAmount
, jumlah pesanan minimum (matawang) yang boleh dilindungi.maxHedgeAmount
, jumlah pesanan maksimum (mata wang) untuk satu lindung nilai.pricePrecisionA
, ketepatan harga pesanan (bilangan tempat perpuluhan) yang diletakkan oleh Exchange A.amountPrecisionA
, jumlah ketepatan pesanan yang diletakkan oleh Exchange A (bilangan tempat perpuluhan).pricePrecisionB
, ketepatan harga pesanan (bilangan tempat perpuluhan) yang diletakkan oleh Exchange B.amountPrecisionB
, jumlah ketepatan pesanan yang diletakkan oleh Bursa B (bilangan tempat perpuluhan).rateA
, penukaran kadar pertukaran objek pertukaran pertama yang ditambah, lalai adalah 1, tidak ditukaran.rateB
, penukaran kadar pertukaran objek pertukaran kedua yang ditambah, lalai adalah 1, tidak ditukaran.Strategi lindung nilai perlu mengekalkan jumlah syiling dalam kedua-dua akaun tidak berubah (iaitu, tidak memegang kedudukan ke arah mana-mana, dan mengekalkan netraliti), jadi perlu ada logik keseimbangan dalam strategi untuk sentiasa mengesan baki.
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
}
Selepas meletakkan pesanan, jika tidak ada pesanan yang selesai, kita perlu membatalkannya tepat pada masanya, dan pesanan tidak boleh disimpan menunggu. operasi ini perlu diproses dalam modul baki dan logik lindung nilai, jadi ia juga perlu untuk merancang fungsi pengeluaran pesanan penuh.
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 menyeimbangkan jumlah syiling, kita perlu mencari harga yang terkumpul kepada sebilangan syiling dalam data kedalaman tertentu, jadi kita memerlukan fungsi seperti itu 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 merancang dan menulis operasi pesanan lindung nilai tertentu, yang perlu direka untuk meletakkan 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()
}
Akhirnya, mari kita selesaikan reka bentuk fungsi keseimbangan, yang agak rumit.
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
// Calculate the currency difference
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("unable to balance")
} else {
// balance order
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("invalid price", price)
}
}
return false
} else if (!(initAccs.length == nowAccs.length && nowAccs.length == depths.length)) {
Log("errors:", "initAccs.length:", initAccs.length, "nowAccs.length:", nowAccs.length, "depths.length:", depths.length)
return true
} else {
return true
}
}
Selepas merancang fungsi ini mengikut keperluan strategi, kemudian mula merancang fungsi utama strategi.
Di platform FMZ, strategi dilaksanakan darimain
Pada permulaanmain
fungsi, kita perlu melakukan beberapa kerja inisialisasi strategi.
Nama objek pertukaran Oleh kerana banyak operasi dalam strategi harus menggunakan objek pertukaran, seperti mendapatkan sebut harga pasaran, meletakkan pesanan dan sebagainya.
var exA = exchanges[0]
var exB = exchanges[1]
Ini memudahkan untuk menulis kod kemudian.
Kadar pertukaran, reka bentuk yang berkaitan dengan ketepatan
// precision, exchange rate settings
if (rateA != 1) {
// set exchange rate A
exA.SetRate(rateA)
Log("Exchange A sets the exchange rate:", rateA, "#FF0000")
}
if (rateB != 1) {
// set exchange rate B
exB.SetRate(rateB)
Log("Exchange B sets the exchange rate:", rateB, "#FF0000")
}
exA.SetPrecision(pricePrecisionA, amountPrecisionA)
exB.SetPrecision(pricePrecisionB, amountPrecisionB)
Jika parameter kadar pertukaranrateA
, rateB
ditetapkan kepada 1 (default adalah 1), iaitu,rateA != 1
ataurateB != 1
tidak akan mencetuskan, jadi penukaran kadar pertukaran tidak akan ditetapkan.
Tetapkan semula semua data
Kadang-kadang ia adalah perlu untuk memadamkan semua log dan membersihkan data yang direkodkan apabila strategi bermula.isReset
, dan merancang kod reset dalam bahagian inisialisasi strategi, contohnya:
if (isReset) { // When isReset is true, reset the data
_G(null)
LogReset(1)
LogProfitReset()
LogVacuum()
Log("reset all data", "#FF0000")
}
Memulihkan data akaun asal, mengemas kini data akaun semasa
Untuk menilai baki, strategi perlu terus mencatat aset akaun awal untuk perbandingan dengan yang semasa.nowAccs
digunakan untuk merakam data akaun semasa, menggunakan fungsi yang kita hanya direkaupdateAccs
untuk mendapatkan data akaun pertukaran semasa.initAccs
digunakan untuk merekodkan status akaun awal (bilangan syiling, bilangan syiling berdenominasi, dan lain-lain di bursa A dan B).initAccs
, gunakan_G()
fungsi untuk memulihkan terlebih dahulu (fungsi _G akan merakam data secara berterusan, dan boleh mengembalikan data yang direkodkan lagi, lihat dokumentasi API untuk butiran:www.fmz.com/api#_gk-v)), jika pertanyaan itu tidak berfungsi, gunakan maklumat akaun semasa untuk menetapkan nilai dan gunakan_G
fungsi untuk merekod.
Seperti kod berikut:
var nowAccs = _C(updateAccs, exchanges)
var initAccs = _G("initAccs")
if (!initAccs) {
initAccs = nowAccs
_G("initAccs", initAccs)
}
Kod dalam gelung utama adalah proses setiap pusingan pelaksanaan logik strategi, yang dijalankan berulang kali untuk membentuk gelung utama strategi.
Dapatkan data pasaran dan menilai kesahihan data pasaran
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 kita boleh melihat bahawa fungsi serentakexchange.Go
dari platform FMZ digunakan untuk membuat objek serentakdepthARoutine
, depthBRoutine
yang memanggilGetDepth()
Apabila kedua-dua objek serentak ini dicipta,GetDepth()
Antara muka dipanggil dengan serta-merta, dan kedua-dua permintaan untuk data kedalaman dihantar ke pertukaran.
Kemudian hubungiwait()
kaedahdepthARoutine
, depthBRoutine
objek untuk mendapatkan data kedalaman.
Selepas mendapatkan data kedalaman, adalah perlu untuk memeriksa data kedalaman untuk menentukan kesahihannya.continue
Perintah akan dicetuskan untuk menjalankan semula gelung utama.
Gunakanspread value
parameter atauspread ratio
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
}
Dari segi parameter, kami telah membuat reka bentuk seperti: parameter FMZ bolehmenunjukkanataubersembunyiberdasarkan parameter, jadi kita boleh membuat parameter untuk memutuskan sama ada untuk menggunakanprice spread
, atauspread ratio
.
ParameterdiffAsPercentage
telah ditambah kepada parameter antara muka strategi. kedua-dua tetapan parameter lain untuk menunjukkan atau menyembunyikan berdasarkan parameter ini adalah:hedgeDiffPrice@!diffAsPercentage
, yang dipaparkan apabiladiffAsPercentage
adalah palsu.hedgeDiffPercentage@diffAsPercentage
, yang dipaparkan apabiladiffAsPercentage
memang benar.
Selepas reka bentuk ini, kami memeriksadiffAsPercentage
parameter, yang merupakan keadaan pencetus lindung nilai berdasarkan nisbah perbezaan harga.diffAsPercentage
Parameter diperiksa, lindung nilai diaktifkan oleh perbezaan harga.
Menentukan syarat pemicu 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 market conditions are met
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("trigger A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, price, amount, nowAccs[1].Balance / price, nowAccs[0].Stocks) // Tips
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 market conditions are met
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("trigger B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, price, amount, nowAccs[0].Balance / price, nowAccs[1].Stocks) // Tips
hedge(exA, exB, price, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
}
Syarat pemicu lindung nilai adalah seperti berikut:
isTrade
Di sini, jika lindung nilai dicetuskan, pembolehubah ditetapkan kepadatrue
. Dan menetapkan semula pembolehubah globallastKeepBalanceTS
kepada 0 (lastKeepBalanceTS digunakan untuk menandakan cap masa operasi imbangan terakhir, menetapkannya kepada 0 akan mencetuskan operasi imbangan dengan segera), dan kemudian membatalkan semua pesanan yang menunggu.Operasi menyeimbangkan
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
}
}
}
Ia dapat dilihat bahawa fungsi penyeimbangan dijalankan secara berkala, tetapi jikalastKeepBalanceTS
Jika nilai semula ke 0 selepas operasi lindung nilai telah dicetuskan, operasi imbangan akan dicetuskan dengan serta-merta.
Maklumat bar status
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",
"current A, Stocks:", nowAccs[0].Stocks, "FrozenStocks:", nowAccs[0].FrozenStocks, "Balance:", nowAccs[0].Balance, "FrozenBalance", nowAccs[0].FrozenBalance, "\n",
"current B, Stocks:", nowAccs[1].Stocks, "FrozenStocks:", nowAccs[1].FrozenStocks, "Balance:", nowAccs[1].Balance, "FrozenBalance", nowAccs[1].FrozenBalance, "\n",
"initial A, Stocks:", initAccs[0].Stocks, "FrozenStocks:", initAccs[0].FrozenStocks, "Balance:", initAccs[0].Balance, "FrozenBalance", initAccs[0].FrozenBalance, "\n",
"initial 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 Bursa A ke Bursa B dan perbezaan harga dari Bursa B ke Bursa A. Dan ia memaparkan spread sasaran lindung nilai semasa, data aset akaun bursa A dan akaun bursa B.
Dari segi parameter, kami merancang parameter nilai kadar penukaran, dan kami juga merancang penukaran kadar pertukaran dalam operasi awal sistem pengubahsuaian.main
Ia harus diperhatikan bahawaSetRate
Fungsi penukaran kadar pertukaran perlu dilaksanakan terlebih dahulu.
Kerana fungsi ini mempengaruhi dua aspek:
BTC_USDT
, unit harga adalahUSDT
, dan mata wang yang tersedia dalam aset akaun jugaUSDT
. Jika saya mahu menukar nilai ke CNY, menetapkanexchange.SetRate(6.8)
dalam kod untuk menukar data yang diperoleh oleh semua fungsi di bawahexchange
objek pertukaran kepada CNY.
Untuk menukar kepada apa mata wang, masukkankadar pertukaran dari mata wang yang berdenominasi semasa kepada mata wang berdenominasi sasarankepadaSetRate
function.Strategi Lengkap:Strategi lindung nilai spot bagi mata wang yang berbeza (Tutorial)