avatar of 发明者量化-小小梦 发明者量化-小小梦
tập trung vào tin nhắn riêng tư
4
tập trung vào
1271
Người theo dõi

Thiết kế chiến lược phòng ngừa rủi ro giao ngay tiền kỹ thuật số (1)

Được tạo ra trong: 2021-07-19 17:38:24, cập nhật trên: 2023-09-20 10:35:16
comments   1
hits   3142

Thiết kế chiến lược phòng ngừa rủi ro giao ngay tiền kỹ thuật số (1)

Thiết kế chiến lược phòng ngừa rủi ro giao ngay tiền kỹ thuật số (1)

Đối với người mới bắt đầu thiết kế chiến lược, chiến lược phòng ngừa rủi ro là một chiến lược đào tạo rất tốt. Bài viết này triển khai một chiến lược phòng ngừa rủi ro tiền kỹ thuật số đơn giản nhưng thực tế, hy vọng rằng người mới bắt đầu có thể học được một số kinh nghiệm thiết kế.

Thiết kế một số chức năng và tham số giao diện chiến lược theo yêu cầu chiến lược

Trước hết, rõ ràng là chiến lược được thiết kế là chiến lược phòng ngừa rủi ro giao ngay tiền kỹ thuật số. Chúng tôi thiết kế chiến lược phòng ngừa rủi ro đơn giản nhất, đó là bán tại sàn giao dịch với giá cao hơn giữa hai sàn giao dịch giao ngay và mua tại sàn giao dịch với giá thấp hơn để kiếm lời. Lấy phần chênh lệch. Khi tất cả các sàn giao dịch có giá cao hơn đều được tính bằng tiền xu (vì tất cả các sàn giao dịch có giá cao hơn đều được bán), và tất cả các sàn giao dịch có giá thấp hơn đều được tính bằng tiền xu (tất cả các sàn giao dịch có giá thấp hơn đều được mua), thì không thể Phòng ngừa rủi ro. Vào thời điểm này, bạn chỉ có thể chờ giá đảo ngược và phòng ngừa rủi ro.

Khi phòng ngừa rủi ro, sàn giao dịch có những hạn chế về độ chính xác của giá và số lượng lệnh, đồng thời cũng có giới hạn về số lượng lệnh tối thiểu. Ngoài giới hạn tối thiểu, chiến lược cũng phải xem xét khối lượng lệnh tối đa để phòng ngừa rủi ro tại một thời điểm. Nếu khối lượng lệnh quá lớn, sẽ không có đủ lệnh trên thị trường. Bạn cũng cần cân nhắc cách chuyển đổi theo tỷ giá hối đoái nếu hai sàn giao dịch có đơn vị tiền tệ khác nhau. Khi phòng ngừa, phí xử lý và trượt giá lệnh đều là chi phí giao dịch. Không thể phòng ngừa miễn là có chênh lệch giá. Do đó, cũng có giá trị kích hoạt để phòng ngừa chênh lệch giá. Khi chênh lệch giá thấp hơn ở một mức độ nhất định, việc phòng ngừa sẽ dẫn đến thua lỗ.

Dựa trên những cân nhắc này, chiến lược cần thiết kế một số thông số:

  • Bảo vệ sự khác biệt:hedgeDiffPrice, khi chênh lệch giá vượt quá giá trị này, hoạt động phòng ngừa rủi ro sẽ được kích hoạt.
  • Số tiền phòng ngừa tối thiểu:minHedgeAmount, số lượng đặt hàng tối thiểu (tính bằng xu) có thể được bảo hiểm.
  • Số tiền phòng ngừa tối đa:maxHedgeAmount, số lượng đặt hàng tối đa (số lượng tiền xu) cho một lần phòng ngừa rủi ro.
  • Độ chính xác của giá:pricePrecisionA, độ chính xác của giá lệnh (số chữ số thập phân) của sàn giao dịch A.
  • Độ chính xác về số lượng đặt hàng:amountPrecisionA, độ chính xác về số lượng đặt hàng (số chữ số thập phân) của sàn giao dịch A.
  • Độ chính xác của giá:pricePrecisionB, độ chính xác của giá lệnh (số chữ số thập phân) của sàn giao dịch B.
  • Độ chính xác về số lượng đặt hàng:amountPrecisionB, độ chính xác về số lượng đặt hàng (số chữ số thập phân) của trao đổi B.
  • Tỷ giá hối đoái:rateA, tỷ giá hối đoái chuyển đổi của đối tượng trao đổi được thêm đầu tiên, giá trị mặc định là 1, không chuyển đổi.
  • Tỷ giá hối đoái của đồng B:rateB, tỷ giá hối đoái chuyển đổi của đối tượng trao đổi được thêm vào thứ hai, giá trị mặc định là 1 và không thực hiện chuyển đổi nào.

Chiến lược phòng ngừa rủi ro cần phải giữ nguyên số lượng tiền trong hai tài khoản (tức là không giữ bất kỳ vị thế định hướng nào và duy trì tính trung lập), do đó cần phải có logic cân bằng trong chiến lược để luôn kiểm tra số dư. Khi kiểm tra số dư, việc lấy dữ liệu tài sản từ hai sàn giao dịch là điều không thể tránh khỏi. Chúng ta cần viết một hàm để sử dụng nó.

  • 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 
    }
    

Nếu lệnh không được thực hiện sau khi đặt, chúng ta cần phải hủy lệnh kịp thời chứ không để lệnh đó tồn đọng. Hoạt động này cần được xử lý trong cả mô-đun cân bằng và logic phòng ngừa, do đó cần thiết kế một chức năng rút lệnh đầy đủ.

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

Khi cân bằng số lượng tiền xu, chúng ta cần tìm giá của một số lượng tiền xu nhất định được tích lũy trong một độ sâu dữ liệu nhất định, vì vậy chúng ta cần một hàm như vậy để xử lý việc này.

  • 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
    }
    

Sau đó, chúng ta cần thiết kế và viết lệnh phòng ngừa cụ thể, cần được thiết kế để là lệnh đồng thời:

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

Cuối cùng, chúng ta hãy hoàn thiện việc thiết kế hàm cân bằng, có phần phức tạp hơn một chút.

  • 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 
      }
    }
    

Bây giờ các chức năng này đã được thiết kế theo yêu cầu của chiến lược, chúng ta có thể bắt đầu thiết kế chức năng chính của chiến lược.

Thiết kế chức năng chính của chiến lược

Trên FMZ chiến lược làmainChức năng bắt đầu thực thi. hiện hữumainKhi bắt đầu hàm, chúng ta cần thực hiện một số khởi tạo chiến lược.

  • Đổi tên đối tượng Bởi vì nhiều hoạt động trong chiến lược đòi hỏi phải sử dụng các đối tượng trao đổi, chẳng hạn như thu thập thông tin thị trường, đặt lệnh, v.v. Vì vậy, sẽ rất phiền phức khi sử dụng tên dài mọi lúc. Bí quyết là sử dụng tên đơn giản thay thế, ví dụ:
  var exA = exchanges[0]
  var exB = exchanges[1]

Điều này giúp cho việc viết code sau này trở nên dễ dàng hơn.

  • Thiết kế liên quan đến tỷ giá hối đoái và độ chính xác
    // 精度,汇率设置
    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)

Nếu tham số tỷ giá hối đoáirateArateBMột số được đặt thành 1 (mặc định là 1), nghĩa làrateA != 1hoặcrateB != 1Sẽ không kích hoạt, do đó sẽ không thiết lập tỷ giá hối đoái.

  • Đặt lại tất cả dữ liệu

Thiết kế chiến lược phòng ngừa rủi ro giao ngay tiền kỹ thuật số (1)

Đôi khi khi bắt đầu một chính sách, cần phải xóa tất cả nhật ký và xóa dữ liệu đã ghi. Bạn có thể thiết kế một tham số giao diện chiến lượcisResetvà sau đó thiết kế mã đặt lại trong phần khởi tạo của chiến lược, ví dụ:

    if (isReset) {   // 当isReset为真时重置数据
        _G(null)
        LogReset(1)
        LogProfitReset()
        LogVacuum()
        Log("重置所有数据", "#FF0000")
    }
  • Khôi phục dữ liệu tài khoản ban đầu và cập nhật dữ liệu tài khoản hiện tại Để xác định số dư, chiến lược cần phải liên tục ghi lại tài sản ban đầu của tài khoản để so sánh với tình hình hiện tại.nowAccsBiến này được sử dụng để ghi lại dữ liệu tài khoản hiện tại bằng cách sử dụng hàm chúng ta vừa thiết kế.updateAccsLấy dữ liệu tài khoản của sàn giao dịch hiện tại.initAccsĐược sử dụng để ghi lại trạng thái tài khoản ban đầu (dữ liệu như số lượng xu và số lượng xu có mệnh giá của Sàn giao dịch A và Sàn giao dịch B). vìinitAccsLần sử dụng đầu tiên_G()Phục hồi chức năng (_Hàm G sẽ ghi dữ liệu liên tục và có thể trả về dữ liệu đã ghi lại một lần nữa. Để biết chi tiết, hãy xem tài liệu API:Liên kết), nếu truy vấn không thành công, hãy sử dụng thông tin tài khoản hiện tại để gán giá trị và sử dụng_GBản ghi chức năng.

Ví dụ, đoạn mã sau:

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

Logic giao dịch, vòng lặp chính trong hàm chính

Mã trong vòng lặp chính là quá trình thực hiện từng vòng logic chiến lược. Việc thực hiện qua lại liên tục tạo nên vòng lặp chính của chiến lược. Chúng ta hãy xem xét luồng thực thi của từng chương trình trong vòng lặp chính.

  • Thu thập dữ liệu thị trường và xác định tính hợp lệ của nó
        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 
        }

Tại đây bạn có thể thấy các chức năng đồng thời của nền tảng FMZ.exchange.Go, đã tạo ra cuộc gọiGetDepth()Đối tượng đồng thời của giao diệndepthARoutinedepthBRoutine. Khi hai đối tượng đồng thời này được tạo ra, hãy gọiGetDepth()Giao diện cũng diễn ra ngay lập tức và hai yêu cầu lấy dữ liệu độ sâu đã được gửi đến sàn giao dịch. Sau đó gọidepthARoutinedepthBRoutineSự vậtwait()Phương pháp để lấy dữ liệu độ sâu.
Sau khi có được dữ liệu độ sâu, cần phải kiểm tra dữ liệu độ sâu để xác định tính hợp lệ của nó. Kích hoạt thực hiện cho dữ liệu bất thườngcontinueCâu lệnh thực hiện lại vòng lặp chính.

  • sử dụng价差值Các tham số hoặc差价比例tham số?
        var targetDiffPrice = hedgeDiffPrice
        if (diffAsPercentage) {
            targetDiffPrice = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentage
        }

Chúng tôi đã thiết kế như vậy về mặt thông số. Các tham số của FMZ có thể dựa trên một tham số nhất địnhtrình diễnhoặctrốn, vì vậy chúng ta có thể tạo một tham số để quyết định có nên sử dụng hay không价格差,vẫn差价比例

Thiết kế chiến lược phòng ngừa rủi ro giao ngay tiền kỹ thuật số (1)

Một tham số đã được thêm vào các tham số giao diện chiến lượcdiffAsPercentage. Hai tham số khác được hiển thị hoặc ẩn dựa trên tham số này được đặt thành: hedgeDiffPrice@!diffAsPercentage,khidiffAsPercentageFalse hiển thị tham số này. hedgeDiffPercentage@diffAsPercentage,khidiffAsPercentageĐúng để hiển thị tham số này. Sau khi thiết kế này, chúng tôi đã kiểm tradiffAsPercentageCác tham số dựa trên tỷ lệ chênh lệch giá làm điều kiện kích hoạt phòng ngừa rủi ro. Bỏ chọndiffAsPercentageTham số này sử dụng chênh lệch giá làm điều kiện kích hoạt phòng ngừa rủi ro.

  • Xác định điều kiện kích hoạt phòng ngừa
        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 
            }            
        }

Có một số điều kiện kích hoạt phòng ngừa rủi ro: 1. Đầu tiên, thỏa mãn chênh lệch giá phòng ngừa. Chỉ có thể phòng ngừa khi chênh lệch giá của thị trường đạt đến các thông số chênh lệch giá đã đặt. 2. Khối lượng phòng ngừa rủi ro trên thị trường phải đạt khối lượng phòng ngừa rủi ro tối thiểu được thiết lập trong các thông số. Vì các sàn giao dịch khác nhau có thể có khối lượng lệnh tối thiểu khác nhau, nên lấy khối lượng nhỏ nhất trong hai khối lượng. 3. Có đủ tài sản trên sàn giao dịch để thực hiện hoạt động bán và có đủ tài sản trên sàn giao dịch để thực hiện hoạt động mua. Khi các điều kiện này được đáp ứng, chức năng phòng ngừa rủi ro sẽ được thực hiện để đặt lệnh phòng ngừa rủi ro. Trước hàm chính, chúng ta đã khai báo một biến trướcisTradeĐược sử dụng để đánh dấu xem việc phòng ngừa có xảy ra hay không. Nếu việc phòng ngừa được kích hoạt, biến này được đặt thànhtrue. Và thiết lập lại các biến toàn cụclastKeepBalanceTSĐặt lastKeepBalanceTS thành 0 (lastKeepBalanceTS được sử dụng để đánh dấu dấu thời gian của hoạt động cân bằng gần đây nhất. Đặt thành 0 sẽ kích hoạt hoạt động cân bằng ngay lập tức), sau đó hủy tất cả các lệnh đang chờ xử lý.

  • Một hành động cân bằng
        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 
                }                
            }            
        }

Bạn có thể thấy rằng hàm cân bằng được thực hiện thường xuyên, nhưng nếu hoạt động phòng ngừa được kích hoạt,lastKeepBalanceTSNếu đặt lại về 0, hoạt động cân bằng sẽ được kích hoạt ngay lập tức. Sau khi số dư thành công, lợi nhuận sẽ được tính toán.

  • Thông tin thanh trạng thái
        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)

Thanh trạng thái không có thiết kế đặc biệt phức tạp. Nó hiển thị thời gian hiện tại, chênh lệch giá từ sàn giao dịch A đến sàn giao dịch B và chênh lệch giá từ sàn giao dịch B đến sàn giao dịch A. Hiển thị mức chênh lệch mục tiêu phòng ngừa rủi ro hiện tại. Hiển thị dữ liệu tài sản của tài khoản giao dịch A và tài khoản giao dịch B.

Xử lý các cặp giao dịch có mệnh giá khác nhau

Về mặt tham số, chúng tôi đã thiết kế tham số giá trị tỷ lệ chuyển đổi khi bắt đầu chiến lượcmainChúng tôi cũng thiết kế tỷ giá hối đoái để thực hiện chức năng ban đầu. Cần lưu ý rằngSetRateChức năng chuyển đổi tỷ giá hối đoái cần phải được thực hiện trước. Bởi vì chức năng này ảnh hưởng đến hai cấp độ:

  • Chuyển đổi giá trong tất cả dữ liệu thị trường, dữ liệu lệnh và dữ liệu vị thế.
  • Chuyển đổi tiền tệ có mệnh giá trong tài sản tài khoản. Ví dụ, cặp giao dịch hiện tại làBTC_USDT, các đơn vị giá làUSDT, tiền tệ có sẵn trong tài sản tài khoản cũng làUSDT. Nếu tôi muốn chuyển đổi sang CNY, hãy đặt nó trong mãexchange.SetRate(6.8)ChỉexchangeDữ liệu thu được từ tất cả các chức năng trong đối tượng trao đổi này đều được chuyển đổi sang CNY. Tại sao lại sử dụng tiền tệ để chuyển đổi?SetRateTruyền hàmTỷ giá hối đoái từ tiền tệ hiện tại sang tiền tệ mục tiêu

Chiến lược hoàn chỉnh:Chiến lược phòng ngừa rủi ro giao ngay cho các loại tiền tệ khác nhau (hướng dẫn)