Untuk pemula dalam merancang strategi, strategi lindung nilai adalah yang sangat baik untuk praktek.
Pertama-tama, kita perlu memastikan bahwa strategi yang akan dirancang adalah strategi lindung nilai spot cryptocurrency. Kami merancang lindung nilai yang paling sederhana. Kami hanya menjual di platform dengan harga yang lebih tinggi antara dua platform spot, dan membeli di platform dengan harga yang lebih rendah untuk mendapatkan spread harga. Ketika platform dengan harga yang lebih tinggi penuh dengan simbol mata uang kutipan (karena harga tinggi, semua simbol mata uang dijual), atau ketika platform dengan harga yang lebih rendah penuh dengan simbol mata uang (karena harga rendah, simbol mata uang dibeli oleh semua aset), itu tidak dapat dilindung nilai. Pada saat ini, Anda hanya dapat menunggu harga terbalik untuk lindung nilai.
Untuk harga pesanan dan jumlah saat lindung nilai, ada batas presisi di setiap platform, dan juga ada batas pada jumlah pesanan minimum. Selain batas minimum, strategi juga perlu mempertimbangkan jumlah pesanan maksimum untuk lindung nilai. Jika jumlah pesanan terlalu besar, pasar tidak akan memiliki volume pesanan yang cukup untuk itu. Juga perlu dipertimbangkan bagaimana mengubah nilai tukar jika kedua platform memiliki mata uang kutipan yang berbeda. Biaya penanganan selama lindung nilai dan tergelincirnya pemegang pesanan adalah semua biaya perdagangan. lindung nilai tidak selalu terjadi selama ada perbedaan harga. Oleh karena itu, spread harga lindung nilai juga memiliki nilai pemicu. Jika lebih rendah dari spread harga tertentu, lindung nilai akan membuat kerugian.
Berdasarkan hal itu, strategi harus dirancang dengan beberapa parameter:
hedgeDiffPrice
; ketika spread melebihi nilai, lindung nilai akan dipicu.minHedgeAmount
, jumlah pesanan minimum (jumlah simbol) yang tersedia untuk lindung nilai.maxHedgeAmount
, jumlah pesanan maksimum (jumlah simbol) yang tersedia untuk lindung nilai.pricePrecisionA
, presisi harga order (angka desimal) dari platform A.amountPrecisionA
, presisi jumlah pesanan (angka desimal) dari platform A.pricePrecisionB
, presisi harga order (angka desimal) dari platform B.amountPrecisionB
, presisi jumlah pesanan (angka desimal) dari platform B.rateA
, nilai tukar yang dikonversi dari objek pertukaran pertama yang ditambahkan; default adalah 1, yang menunjukkan tidak akan dikonversi.rateB
, nilai tukar yang dikonversi dari objek pertukaran kedua yang ditambahkan; default adalah 1, yang menunjukkan tidak akan dikonversi.Strategi lindung nilai perlu menjaga jumlah simbol mata uang dari kedua akun tidak berubah (yaitu, tidak memegang posisi arah, dan menjaga netral), sehingga perlu ada logika keseimbangan dalam strategi untuk selalu mendeteksi saldo.
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
}
Setelah menempatkan order, jika tidak ada order yang dieksekusi, kita perlu membatalkannya tepat waktu, dan order tidak dapat tetap menunggu. operasi ini perlu diproses baik dalam modul saldo dan logika lindung nilai, jadi juga perlu untuk merancang fungsi membatalkan semua order.
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)
}
}
})
}
Saat menyeimbangkan jumlah simbol mata uang, kita perlu menemukan harga dengan jumlah tertentu dalam data kedalaman tertentu, jadi kita perlu fungsi seperti ini untuk menanganinya.
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 dirancang untuk secara bersamaan menempatkan pesanan:
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 desain fungsi keseimbangan, yang sedikit lebih 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 currency spread
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("cannot balance")
} else {
// balanced ordering
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("error:", "initAccs.length:", initAccs.length, "nowAccs.length:", nowAccs.length, "depths.length:", depths.length)
return true
} else {
return true
}
}
Fungsi-fungsi ini telah dirancang sesuai dengan persyaratan strategi, dan kita dapat mulai merancang fungsi utama strategi.
Di FMZ, strategi dilaksanakan darimain
Pada awalmain
fungsi, kita perlu melakukan beberapa inisialisasi strategi.
Nama Obyek Exchange Untuk banyak operasi dalam strategi menggunakan objek pertukaran, seperti mendapatkan penawaran pasar, menempatkan pesanan, dan sebagainya, Jadi akan tidak nyaman untuk menggunakan nama yang lebih panjang setiap kali, trik kecil saya adalah untuk menggunakan nama pendek sederhana sebagai gantinya, misalnya:
var exA = exchanges[0]
var exB = exchanges[1]
Kemudian, akan lebih nyaman untuk menulis kode nanti.
Nilai tukar & Keakuratan
// settings of precision and exchange rate
if (rateA != 1) {
// set exchange rate A
exA.SetRate(rateA)
Log("Platform A sets exchange rate:", rateA, "#FF0000")
}
if (rateB != 1) {
// set exchange rate B
exB.SetRate(rateB)
Log("Platform B sets exchange rate:", rateB, "#FF0000")
}
exA.SetPrecision(pricePrecisionA, amountPrecisionA)
exB.SetPrecision(pricePrecisionB, amountPrecisionB)
Jika salah satu parameter nilai tukar, yaiturateA
danrateB
, ditetapkan menjadi 1 (default adalah 1), yaitu,rateA != 1
ataurateB != 1
berarti tidak dipicu, dan nilai tukar tidak dapat dikonversi.
Setel kembali semua tanggal
Kadang-kadang, perlu untuk menghapus semua log dan vakum catatan data ketika strategi dimulai.isReset
, dan kemudian merancang kode reset di bagian inisialisasi strategi, misalnya:
if (isReset) { // when "isReset" is true, reset the data
_G(null)
LogReset(1)
LogProfitReset()
LogVacuum()
Log("Reset all data", "#FF0000")
}
Pulihkan Data Akun Awal dan Perbarui Data Akun Saat Ini
Untuk menilai saldo, strategi perlu terus mencatat kondisi aset rekening awal untuk perbandingan dengan yang saat ini.nowAccs
digunakan untuk mencatat data rekening arus.updateAccs
fungsi yang baru saja kami rancang untuk mendapatkan data akun dari platform saat ini.initAccs
digunakan untuk mencatat status akun awal (data seperti jumlah simbol mata uang dari kedua A dan B, jumlah mata uang kutipan, dll.).initAccs
, pertama-tama gunakan_G()
fungsi untuk mengembalikan (fungsi _G akan mencatat data secara terus menerus, dan dapat mengembalikan data yang tercatat lagi; baca dokumentasi API untuk rincian:link).
Jika Anda tidak dapat menanyakan data, gunakan informasi rekening current untuk menetapkan dan menggunakan_G()
fungsi untuk merekam.
Seperti kode berikut:
var nowAccs = _C(updateAccs, exchanges)
var initAccs = _G("initAccs")
if (!initAccs) {
initAccs = nowAccs
_G("initAccs", initAccs)
}
Kode dalam loop utama adalah proses dari setiap putaran eksekusi logika strategi, dan eksekusi berulang tanpa henti membangun strategi loop utama.
Dapatkan Kutipan Pasar dan Hukum Validitasnya
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 dapat melihat bahwa fungsi paralelexchange.Go
dari platform FMZ digunakan untuk membuat objek bersamaandepthARoutine
dandepthBRoutine
yang disebutGetDepth()
Ketika dua objek bersamaan ini dibuat,GetDepth()
interface dipanggil segera, dan kedua permintaan untuk data kedalaman dikirim ke platform.
Lalu, hubungiwait()
metode objekdepthARoutine
dan objekdepthBRoutine
untuk mendapatkan data kedalaman.
Setelah mendapatkan data kedalaman, perlu untuk memeriksa data kedalaman untuk menilai keabsahannya.continue
pernyataan dipicu untuk mengeksekusi kembali loop utama.
Penggunaanprice spread
atauspread ratio
?
var targetDiffPrice = hedgeDiffPrice
if (diffAsPercentage) {
targetDiffPrice = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentage
}
Dalam hal parameter, kami telah membuat desain seperti. parameter FMZ dapatpertunjukanataumenyembunyikanberdasarkan parameter, sehingga kita dapat membuat parameter untuk memutuskan apakah untuk menggunakanprice spread
, atauspread ratio
.
ParameterdiffAsPercentage
telah ditambahkan ke parameter antarmuka strategi. dua parameter lainnya, yang akan menunjukkan atau menyembunyikan berdasarkan parameter ditetapkan sebagai:hedgeDiffPrice@!diffAsPercentage
; ketikadiffAsPercentage
adalah palsu, itu akan ditunjukkan.hedgeDiffPercentage@diffAsPercentage
; ketikadiffAsPercentage
adalah benar, itu akan ditampilkan.
Setelah desain, kami telah memeriksadiffAsPercentage
parameter, yaitu menggunakan rasio spread sebagai kondisi pemicu lindung nilai.diffAsPercentage
parameter tidak diperiksa, spread harga digunakan sebagai kondisi pemicu lindung nilai.
Hakim Hedge Trigger
if (depthA.Bids[0].Price - depthB.Asks[0].Price > targetDiffPrice && Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount) >= minHedgeAmount) { // A -> B market condition satisfied
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("triggerA->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, price, amount, nowAccs[1].Balance / price, nowAccs[0].Stocks) // prompt message
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 condition satisfied
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("triggerB->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, price, amount, nowAccs[0].Balance / price, nowAccs[1].Stocks) // prompt message
hedge(exA, exB, price, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
}
Ada beberapa kondisi pemicu untuk lindung nilai: 1.Pertama, memenuhi spread lindung nilai; hanya ketika spread pasar memenuhi parameter spread yang ditetapkan, lindung nilai dapat dimungkinkan.
2.Jumlah lindung nilai pasar harus memenuhi jumlah lindung nilai minimum yang ditetapkan dalam parameter.Karena jumlah pesanan minimum dari platform yang berbeda, yang terkecil dari keduanya harus diambil.
3.Aset di platform dengan operasi jual cukup untuk menjual, dan aset di platform dengan operasi pembelian cukup untuk membeli. Ketika kondisi ini terpenuhi, eksekusi fungsi lindung nilai untuk menempatkan pesanan dengan lindung nilai. Sebelum fungsi utama, kita menyatakan variabelisTrade
Di sini, jika lindung nilai dipicu, variabel diatur untuktrue
Dan reset variabel globallastKeepBalanceTS
ke 0 (lastKeepBalanceTS digunakan untuk menandai timestamp dari operasi saldo terakhir, dan mengaturnya ke 0 akan memicu operasi saldo segera), dan kemudian membatalkan semua pesanan yang menunggu.
Operasi Saldo
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
}
}
}
Hal ini dapat dilihat bahwa fungsi keseimbangan dijalankan secara berkala, tetapi jikalastKeepBalanceTS
Setelah operasi lindung nilai ditetapkan kembali ke 0 setelah operasi lindung nilai dipicu, operasi saldo akan dipicu segera.
Informasi 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",
"currentA,Stocks:", nowAccs[0].Stocks, "FrozenStocks:", nowAccs[0].FrozenStocks, "Balance:", nowAccs[0].Balance, "FrozenBalance", nowAccs[0].FrozenBalance, "\n",
"currentB,Stocks:", nowAccs[1].Stocks, "FrozenStocks:", nowAccs[1].FrozenStocks, "Balance:", nowAccs[1].Balance, "FrozenBalance", nowAccs[1].FrozenBalance, "\n",
"initialA,Stocks:", initAccs[0].Stocks, "FrozenStocks:", initAccs[0].FrozenStocks, "Balance:", initAccs[0].Balance, "FrozenBalance", initAccs[0].FrozenBalance, "\n",
"initialB,Stocks:", initAccs[1].Stocks, "FrozenStocks:", initAccs[1].FrozenStocks, "Balance:", initAccs[1].Balance, "FrozenBalance", initAccs[1].FrozenBalance)
Bar status tidak dirancang untuk menjadi sangat rumit. Ini menampilkan waktu saat ini, spread harga dari platform A ke platform B serta spread harga dari B ke A; juga menampilkan spread target lindung nilai saat ini, data aset akun platform A, dan data aset akun platform B.
Dalam hal parameter, kami merancang parameter konversi nilai nilai nilai tukar, dan kami juga telah merancang konversi nilai tukar dalam operasi awal darimain
Hal ini harus dicatat bahwaSetRate
Fungsi konversi nilai tukar harus dijalankan terlebih dahulu.
Untuk fungsi akan mempengaruhi dua aspek:
Misalnya, pasangan perdagangan saat ini adalahBTC_USDT
, satuan harga adalahUSDT
, dan mata uang penawaran yang tersedia dalam aset rekening jugaUSDT
Jika saya ingin mengkonversi nilai aset ke CNY, aturexchange.SetRate(6.8)
dalam kode untuk mengubah data yang diperoleh oleh semua fungsi di bawahexchange
objek, dan kemudian mengubahnya menjadi CNY.
Untuk mengkonversi ke mata uang penawaran, impornilai tukar dari mata uang penawaran saat ini ke mata uang penawaran targetke dalamSetRate
function.
Strategi lengkap:Strategi Hedge Spot dari Mata Uang Kuotasi yang Berbeda (Teaching)