avatar of 发明者量化-小小梦 发明者量化-小小梦
fokus pada Pesan pribadi
4
fokus pada
1271
Pengikut

Desain strategi lindung nilai spot mata uang digital (1)

Dibuat di: 2021-07-19 17:38:24, diperbarui pada: 2023-09-20 10:35:16
comments   1
hits   3142

Desain strategi lindung nilai spot mata uang digital (1)

Desain strategi lindung nilai spot mata uang digital (1)

Bagi pemula dalam desain strategi, strategi lindung nilai adalah strategi pelatihan yang sangat baik. Artikel ini menerapkan strategi lindung nilai spot mata uang digital yang sederhana namun nyata, dengan harapan para pemula dapat mempelajari beberapa pengalaman desain.

Merancang beberapa fungsi dan parameter antarmuka strategi sesuai dengan persyaratan strategi

Pertama-tama, jelas bahwa strategi yang akan dirancang adalah strategi lindung nilai spot mata uang digital. Kami merancang strategi lindung nilai yang paling sederhana, yaitu menjual di bursa dengan harga lebih tinggi antara dua bursa spot dan membeli di bursa dengan harga lebih rendah. untuk mendapatkan keuntungan. Ambil selisihnya. Bila semua bursa dengan harga lebih tinggi didenominasi dalam koin (karena semua koin dengan harga lebih tinggi dijual), dan semua bursa dengan harga lebih rendah didenominasi dalam koin (semua koin dengan harga lebih rendah dibeli), maka mustahil untuk melakukan Lindung Nilai. Saat ini, Anda hanya dapat menunggu harga berbalik dan melakukan lindung nilai.

Saat melakukan lindung nilai, bursa memiliki batasan akurasi pada harga dan kuantitas pesanan, dan ada pula batasan kuantitas pesanan minimum. Selain batas minimum, strategi juga harus mempertimbangkan volume pesanan maksimum untuk lindung nilai pada satu waktu. Jika volume pesanan terlalu besar, tidak akan ada cukup pesanan di pasar. Anda juga perlu mempertimbangkan cara mengonversi menggunakan nilai tukar jika kedua nilai tukar menggunakan mata uang yang berbeda. Saat melakukan lindung nilai, biaya penanganan dan slippage pada pesanan merupakan biaya transaksi. Tidak mungkin untuk melakukan lindung nilai selama ada perbedaan harga. Oleh karena itu, ada juga nilai pemicu untuk melakukan lindung nilai terhadap perbedaan harga. Ketika perbedaan harga lebih rendah dari tingkat tertentu, lindung nilai akan mengakibatkan kerugian.

Berdasarkan pertimbangan ini, strategi perlu merancang beberapa parameter:

  • Melindungi perbedaan:hedgeDiffPrice, ketika perbedaan harga melampaui nilai tersebut, maka operasi lindung nilai akan dipicu.
  • Jumlah lindung nilai minimum:minHedgeAmount, jumlah pesanan minimum (dalam koin) yang dapat dilindung nilai.
  • Jumlah lindung nilai maksimum:maxHedgeAmount, jumlah pesanan maksimum (jumlah koin) untuk satu lindung nilai.
  • Akurasi Harga:pricePrecisionA, ketepatan harga pesanan (jumlah tempat desimal) pertukaran A.
  • Akurasi jumlah pesanan:amountPrecisionA, ketepatan kuantitas pesanan (jumlah tempat desimal) pertukaran A.
  • Akurasi Harga:pricePrecisionB, ketepatan harga pesanan (jumlah tempat desimal) bursa B.
  • Ketepatan jumlah pesanan:amountPrecisionB, ketepatan kuantitas pesanan (jumlah tempat desimal) pertukaran B.
  • Nilai Tukar:rateA, konversi nilai tukar dari objek pertukaran yang ditambahkan pertama, nilai default adalah 1, tidak ada konversi.
  • Nilai tukar mata uang B :rateB, konversi nilai tukar objek pertukaran tambahan kedua, nilai default adalah 1 dan tidak ada konversi yang dilakukan.

Strategi lindung nilai perlu menjaga jumlah koin di kedua akun tidak berubah (yaitu, tidak memegang posisi arah apa pun dan mempertahankan netralitas), jadi perlu ada logika keseimbangan dalam strategi untuk selalu memeriksa saldo. Saat memeriksa saldo, tidak dapat dihindari untuk memperoleh data aset dari dua bursa. Kita perlu menulis fungsi untuk menggunakannya.

  • updateAccs
    
    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 suatu pesanan tidak dieksekusi setelah ditempatkan, kami perlu membatalkannya tepat waktu dan tidak membiarkannya tertunda. Operasi ini perlu diproses baik dalam modul keseimbangan maupun dalam logika lindung nilai, sehingga fungsi penarikan pesanan lengkap perlu dirancang.

  • cancelAll
    
    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 koin, kita perlu menemukan harga sejumlah koin tertentu yang terakumulasi dalam kedalaman data tertentu, jadi kita perlu fungsi seperti itu untuk menanganinya.

  • getDepthPrice
    
    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 menuliskan operasi perintah lindung nilai yang spesifik, yang perlu dirancang agar menjadi perintah bersamaan:

  • hedge
    
    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()
    }
    

Terakhir, mari selesaikan desain fungsi keseimbangan yang sedikit 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 setelah fungsi-fungsi ini dirancang sesuai dengan persyaratan strategi, kita dapat mulai merancang fungsi utama strategi.

Desain fungsi utama strategi

Di FMZ strateginya adalahmainFungsi mulai dijalankan. adamainPada awal fungsi kita perlu melakukan beberapa inisialisasi strategi.

  • Tukar nama objek Karena banyak operasi dalam strategi memerlukan penggunaan objek pertukaran, seperti memperoleh informasi pasar, menempatkan pesanan, dan lain-lain. Jadi akan merepotkan jika menggunakan nama yang panjang setiap saat. Triknya adalah menggunakan nama yang sederhana, misalnya:
  var exA = exchanges[0]
  var exB = exchanges[1]

Ini membuatnya sangat nyaman untuk menulis kode nantinya.

  • Desain terkait nilai tukar dan akurasi
    // 精度,汇率设置
    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 nilai tukarrateArateBBeberapa diatur ke 1 (defaultnya adalah 1), yaiturateA != 1ataurateB != 1Tidak akan dipicu, jadi tidak ada konversi nilai tukar yang akan ditetapkan.

  • Setel ulang semua data

Desain strategi lindung nilai spot mata uang digital (1)

Kadang-kadang ketika suatu kebijakan dimulai, perlu untuk menghapus semua log dan membersihkan data yang tercatat. Anda dapat merancang parameter antarmuka strategiisReset, lalu rancang kode reset di bagian inisialisasi strategi, misalnya:

    if (isReset) {   // 当isReset为真时重置数据
        _G(null)
        LogReset(1)
        LogProfitReset()
        LogVacuum()
        Log("重置所有数据", "#FF0000")
    }
  • Mengembalikan data akun awal dan memperbarui data akun saat ini Untuk menentukan saldo, strategi perlu terus mencatat aset akun awal untuk dibandingkan dengan situasi saat ini.nowAccsVariabel ini digunakan untuk mencatat data akun berjalan, menggunakan fungsi yang baru saja kita rancang.updateAccsDapatkan data akun bursa saat ini.initAccsDigunakan untuk mencatat status awal akun (data seperti jumlah koin dan jumlah koin yang berdenominasi di Bursa A dan Bursa B). untukinitAccsPenggunaan pertama_G()Pemulihan fungsi (_Fungsi G akan merekam data secara terus-menerus dan dapat mengembalikan data yang direkam lagi. Untuk detailnya, lihat dokumentasi API:Link), jika kueri gagal, gunakan informasi akun saat ini untuk menetapkan nilai dan gunakan_GRekaman fungsi.

Misalnya, kode berikut:

    var nowAccs = _C(updateAccs, exchanges)
    var initAccs = _G("initAccs")
    if (!initAccs) {
        initAccs = nowAccs
        _G("initAccs", initAccs)
    }

Logika perdagangan, loop utama dalam fungsi utama

Kode dalam loop utama adalah proses setiap putaran eksekusi logika strategi. Eksekusi timbal balik yang berkelanjutan merupakan loop utama strategi. Mari kita lihat alur tiap eksekusi program dalam putaran utama.

  • Dapatkan data pasar dan tentukan 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 fungsi bersamaan pada platform FMZ.exchange.Go, membuat panggilanGetDepth()Objek konkurensi dari antarmukadepthARoutinedepthBRoutine. Ketika dua objek bersamaan ini dibuat, panggilGetDepth()Antarmuka juga terjadi segera, dan dua permintaan untuk memperoleh data kedalaman dikirim ke bursa. Lalu panggildepthARoutinedepthBRoutineObyekwait()Metode untuk memperoleh data kedalaman.
Setelah memperoleh data kedalaman, maka perlu dilakukan pengecekan data kedalaman untuk mengetahui keabsahannya. Pemicu eksekusi untuk anomali datacontinuePernyataan mengeksekusi ulang loop utama.

  • menggunakan价差值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 desain seperti itu dalam hal parameter. Parameter FMZ dapat didasarkan pada parameter tertentumenunjukkanataubersembunyi, jadi kita bisa membuat parameter untuk memutuskan apakah akan menggunakan价格差,tetap差价比例

Desain strategi lindung nilai spot mata uang digital (1)

Parameter telah ditambahkan ke parameter antarmuka strategidiffAsPercentage. Dua parameter lain yang ditampilkan atau disembunyikan berdasarkan parameter ini ditetapkan ke: hedgeDiffPrice@!diffAsPercentage,KapandiffAsPercentageFalse menampilkan parameter ini. hedgeDiffPercentage@diffAsPercentage,KapandiffAsPercentageBenar untuk menampilkan parameter ini. Setelah desain ini, kami memeriksadiffAsPercentageParameter didasarkan pada rasio perbedaan harga sebagai kondisi pemicu lindung nilai. Hapus tanda centangdiffAsPercentageParameternya adalah menggunakan perbedaan harga sebagai kondisi pemicu lindung nilai.

  • Tentukan kondisi 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 盘口条件满足            
            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 
            }            
        }

Ada beberapa kondisi pemicu lindung nilai: 1. Pertama, penuhi selisih harga lindung nilai. Lindung nilai hanya dapat dilakukan jika selisih harga pasar memenuhi parameter selisih harga yang ditetapkan. 2. Volume lindung nilai di pasar harus memenuhi volume lindung nilai minimum yang ditetapkan dalam parameter. Karena bursa yang berbeda mungkin memiliki volume pesanan minimum yang berbeda, maka volume terkecil dari keduanya harus diambil. 3. Terdapat cukup aset di bursa untuk operasi penjualan guna menjual, dan terdapat cukup aset di bursa untuk operasi pembelian guna membeli. Bila kondisi ini terpenuhi, fungsi lindung nilai dijalankan untuk menempatkan perintah lindung nilai. Sebelum fungsi utama kita mendeklarasikan variabel terlebih dahuluisTradeDigunakan untuk menandai apakah lindung nilai terjadi. Jika lindung nilai dipicu, variabel ini disetel ketrue. Dan mengatur ulang variabel globallastKeepBalanceTSTetapkan lastKeepBalanceTS ke 0 (lastKeepBalanceTS digunakan untuk menandai stempel waktu operasi penyeimbangan terkini. Menetapkannya ke 0 akan segera memicu operasi penyeimbangan), lalu batalkan semua pesanan yang tertunda.

  • Tindakan penyeimbangan
        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 dapat melihat bahwa fungsi keseimbangan dijalankan secara teratur, tetapi jika operasi lindung nilai dipicu,lastKeepBalanceTSJika diatur ulang ke 0, operasi penyeimbangan akan segera dipicu. Setelah saldo berhasil, maka akan dihitung keuntungannya.

  • Informasi bilah 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", 
            "当前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)

Bilah status tidak terlalu rumit dalam desainnya. Bilah ini menampilkan waktu saat ini, perbedaan harga dari bursa A ke bursa B, dan perbedaan harga dari bursa B ke bursa A. Menampilkan sebaran target lindung nilai saat ini. Menampilkan data aset akun bursa A dan akun bursa B.

Pemrosesan pasangan perdagangan dengan mata uang denominasi berbeda

Dari segi parameter, kami merancang parameter nilai tingkat konversi di awal strategimainKami juga merancang konversi nilai tukar untuk operasi awal fungsi tersebut. Perlu dicatat bahwaSetRateFungsi konversi nilai tukar perlu dijalankan terlebih dahulu. Karena fungsi ini mempengaruhi dua level:

  • Konversi harga di semua data pasar, data pesanan, dan data posisi.
  • Konversi mata uang yang dinominasikan dalam aset akun. Misalnya, pasangan perdagangan saat ini adalahBTC_USDT, satuan harga adalahUSDTMata uang yang tersedia dalam aset akun adalahUSDT. Jika saya ingin mengonversinya ke CNY, atur dalam kodeexchange.SetRate(6.8)HanyaexchangeData yang diperoleh oleh semua fungsi di bawah objek pertukaran ini dikonversi ke CNY. Mengapa mata uang digunakan untuk konversi?SetRateFungsi lewatNilai tukar dari mata uang saat ini ke mata uang target

Strategi lengkapnya:Strategi lindung nilai spot untuk berbagai mata uang (tutorial)