avatar of 发明者量化-小小梦 发明者量化-小小梦
집중하다 사신
4
집중하다
1271
수행원

디지털화폐 스팟헤징 전략설계(1)

만든 날짜: 2021-07-19 17:38:24, 업데이트 날짜: 2023-09-20 10:35:16
comments   1
hits   3142

디지털화폐 스팟헤징 전략설계(1)

디지털화폐 스팟헤징 전략설계(1)

전략 설계 초보자에게 헤지 전략은 매우 좋은 훈련 전략입니다. 이 글에서는 초보자들이 어느 정도 설계 경험을 얻을 수 있기를 바라며 간단하면서도 현실적인 디지털 통화 현물 헤지 전략을 구현해 보겠습니다.

전략 요구 사항에 따라 일부 기능 및 전략 인터페이스 매개변수를 설계합니다.

우선, 설계할 전략은 디지털 통화 현물 헤지 전략이라는 것이 분명합니다. 우리는 가장 간단한 헤지 전략을 설계하는데, 이는 두 현물 거래소 사이에서 가격이 더 높은 거래소에서 매도하고 가격이 더 낮은 거래소에서 매수하는 것입니다. 이익을 내기 위해. 차액을 가져가세요. 가격이 높은 모든 거래소가 코인으로 표시되어 있고(가격이 높은 모든 코인이 매도되기 때문), 가격이 낮은 모든 거래소가 코인으로 표시되어 있을 때(가격이 낮은 모든 코인이 매수되기 때문), 헤지는 불가능합니다. 지금 당장은 가격이 반전되어 헤지할 때까지 기다릴 수 있습니다.

헤지거래 시, 거래소는 주문 가격과 수량에 대한 정확성 제한을 두고 있으며, 최소 주문 수량에도 제한이 있습니다. 최소 한도 외에도 전략은 한 번에 헤지하기 위한 최대 주문량도 고려해야 합니다. 주문량이 너무 많으면 시장에 충분한 주문이 없을 것입니다. 두 거래소가 서로 다른 통화로 운영되는 경우 환율을 사용하여 어떻게 변환할 것인지도 고려해야 합니다. 헤지할 때, 처리 수수료와 주문의 미끄러짐은 모두 거래 비용입니다. 가격 차이가 있는 한 헤지할 수 없습니다. 따라서 가격 차이를 헤지하기 위한 트리거 값도 있습니다. 가격 차이가 다음보다 낮을 때 특정 수준에서 헤지거래를 하면 손실이 발생합니다.

이러한 고려 사항에 따라 전략은 여러 매개변수를 설계해야 합니다.

  • 차이 헤지:hedgeDiffPrice가격 차이가 이 값을 초과하면 헤지 거래가 시작됩니다.
  • 최소 헤지 금액:minHedgeAmount헤지 가능한 최소 주문 수량(코인).
  • 최대 헤지 금액:maxHedgeAmount, 한 헤지에 대한 최대 주문 수량(코인 수).
  • 가격 정확도:pricePrecisionA, 거래소 A의 주문 가격 정확도(소수점 자릿수).
  • 주문 수량 정확도:amountPrecisionA, 거래소 A의 주문 수량 정밀도(소수점 자릿수).
  • BPrice 정확도:pricePrecisionB, 거래소 B의 주문 가격 정확도(소수점 자릿수).
  • 국경 수량 정확도:amountPrecisionB, 거래소 B의 주문 수량 정밀도(소수점 자릿수).
  • A 환율:rateA, 첫번째 추가된 환전객체의 환율 변환, 기본값은 1이며 변환 없음.
  • B환전의 환율 :rateB, 두 번째로 추가된 환전 대상의 환율 변환, 기본값은 1이며, 아무런 변환도 수행되지 않습니다.

헤지 전략은 두 계좌의 코인 수를 변경하지 않고 유지해야 합니다(즉, 어떠한 방향성 포지션도 취하지 않고 중립을 유지해야 함). 따라서 전략 내에 항상 잔액을 확인하기 위한 균형 논리가 있어야 합니다. 잔고를 확인할 때, 두 거래소의 자산 데이터를 얻는 것은 불가피합니다. 이를 사용하려면 함수를 작성해야 합니다.

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

주문이 접수된 후 실행되지 않을 경우, 주문을 제때 취소해야 하며, 보류 상태로 두어서는 안 됩니다. 이러한 작업은 잔액 모듈과 헤지 로직에서 모두 처리되어야 하므로 전체 주문 철회 기능을 설계해야 합니다.

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

코인의 개수를 균형화할 때는 특정 깊이의 데이터에 누적된 특정 개수의 코인에 대한 가격을 찾아야 하므로 이를 처리할 수 있는 함수가 필요합니다.

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

그런 다음 동시 주문이 되도록 설계해야 하는 특정 헤지 주문 작업을 설계하고 작성해야 합니다.

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

마지막으로, 조금 복잡한 균형 함수의 설계를 완성해 보겠습니다.

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

이제 이러한 기능이 전략적 요구 사항에 따라 설계되었으므로 전략의 주요 기능을 설계할 수 있습니다.

전략 주요 기능 설계

FMZ의 전략은 다음과 같습니다.main함수가 실행되기 시작합니다. 존재하다main함수 시작부분에서 몇 가지 전략 초기화를 해야 합니다.

  • 교환 객체 이름 전략에서 많은 작업에는 시장 정보 획득, 주문 실행 등 교환 대상의 사용이 필요합니다. 그래서 매번 긴 이름을 사용하는 것은 번거로울 것입니다. 대신 간단한 이름을 사용하는 것이 요령입니다. 예를 들어:
  var exA = exchanges[0]
  var exB = exchanges[1]

이렇게 하면 나중에 코드를 매우 편안하게 작성할 수 있습니다.

  • 환율 및 정확도 관련 설계
    // 精度,汇率设置
    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)

환율 매개변수의 경우rateArateB일부는 1로 설정됩니다(기본값은 1입니다).rateA != 1또는rateB != 1트리거되지 않으므로 환율 변환이 설정되지 않습니다.

  • 모든 데이터 재설정

디지털화폐 스팟헤징 전략설계(1)

정책이 활성화되면 모든 로그를 삭제하고 기록된 데이터를 지워야 하는 경우가 있습니다. 전략 인터페이스 매개변수를 디자인할 수 있습니다.isReset그런 다음 전략의 초기화 섹션에서 재설정 코드를 설계합니다. 예:

    if (isReset) {   // 当isReset为真时重置数据
        _G(null)
        LogReset(1)
        LogProfitReset()
        LogVacuum()
        Log("重置所有数据", "#FF0000")
    }
  • 초기 계정 데이터 복원 및 현재 계정 데이터 업데이트 잔액을 결정하기 위해서는 전략에 따라 현재 상황과 비교하기 위해 초기 계정 자산을 지속적으로 기록해야 합니다.nowAccs이 변수는 방금 설계한 함수를 사용하여 현재 계정 데이터를 기록하는 데 사용됩니다.updateAccs현재 거래소의 계정 데이터를 가져옵니다.initAccs최초 계좌 상태(거래소 A, 거래소 B의 코인 개수, 표시 코인 개수 등의 데이터)를 기록하는 데 사용됩니다. ~을 위한initAccs첫 번째 사용_G()기능 복구 (_G 함수는 데이터를 지속적으로 기록하고 기록된 데이터를 다시 반환할 수 있습니다. 자세한 내용은 API 설명서를 참조하세요.링크), 쿼리가 실패하면 현재 계정 정보를 사용하여 값을 지정하고 사용합니다._G기능 기록.

예를 들어, 다음 코드는 다음과 같습니다.

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

거래 로직, 메인 함수의 메인 루프

메인 루프의 코드는 전략 로직의 각 라운드 실행 프로세스입니다. 지속적인 왕복 실행은 전략의 메인 루프를 구성합니다. 메인 루프에서 프로그램의 각 실행 흐름을 살펴보겠습니다.

  • 시장 데이터를 얻고 유효성을 판단합니다.
        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 
        }

여기서 FMZ 플랫폼의 동시 기능을 확인할 수 있습니다.exchange.Go, 호출을 생성했습니다GetDepth()인터페이스의 동시성 객체depthARoutinedepthBRoutine. 이 두 개의 동시 객체가 생성되면 다음을 호출합니다.GetDepth()또한 인터페이스가 즉시 이루어졌으며, 심도 데이터를 얻기 위한 두 가지 요청이 거래소로 전송되었습니다. 그 다음에는 전화하세요depthARoutinedepthBRoutine물체wait()깊이 데이터를 얻는 방법.
심도 데이터를 얻은 후에는 심도 데이터의 유효성을 검사해야 합니다. 데이터 이상에 대한 트리거 실행continue해당 문장은 메인 루프를 다시 실행합니다.

  • 사용价差值매개변수 또는差价比例매개변수?
        var targetDiffPrice = hedgeDiffPrice
        if (diffAsPercentage) {
            targetDiffPrice = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentage
        }

우리는 매개변수 측면에서 그런 디자인을 만들었습니다. FMZ의 매개변수는 특정 매개변수를 기반으로 할 수 있습니다.보여주다또는숨다, 그래서 우리는 사용할지 여부를 결정하기 위한 매개변수를 만들 수 있습니다价格差,아직差价比例

디지털화폐 스팟헤징 전략설계(1)

전략 인터페이스 매개변수에 매개변수가 추가되었습니다.diffAsPercentage. 이 매개변수에 따라 표시되거나 숨겨지는 다른 두 매개변수는 다음과 같이 설정됩니다. hedgeDiffPrice@!diffAsPercentage,언제diffAsPercentageFalse는 이 매개변수를 표시합니다. hedgeDiffPercentage@diffAsPercentage,언제diffAsPercentage이 매개변수를 표시하려면 true를 지정합니다. 이 디자인을 한 후, 우리는 확인했습니다.diffAsPercentage매개변수는 헤지 트리거 조건으로 가격 차이 비율을 기반으로 합니다. 체크 해제diffAsPercentage매개변수는 가격 차이를 헤지 트리거 조건으로 사용하는 것입니다.

  • 헤지 트리거 조건 결정
        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 
            }            
        }

헤지 트리거 조건에는 여러 가지가 있습니다. 1. 첫째, 헤지 가격 차이를 충족합니다. 헤지는 시장의 가격 차이가 설정된 가격 차이 매개변수를 충족할 때만 수행할 수 있습니다. 2. 시장의 헤지 거래량은 매개변수에 설정된 최소 헤지 거래량을 충족해야 합니다. 거래소마다 최소 주문량이 다를 수 있으므로 두 거래량 중 가장 작은 것을 선택해야 합니다. 3. 거래소에는 판매 작업을 위한 자산이 충분히 있어서 판매할 수 있고, 거래소에는 매수 작업을 위한 자산이 충분히 있어서 매수할 수 있습니다. 이러한 조건이 충족되면 헤지 기능이 실행되어 헤지 주문이 실행됩니다. 메인 함수 전에 미리 변수를 선언해두었습니다.isTrade헤지 발생 여부를 표시하는 데 사용됩니다. 헤지 발생 시 이 변수는 다음과 같이 설정됩니다.true. 그리고 전역 변수를 재설정합니다lastKeepBalanceTSlastKeepBalanceTS를 0으로 설정합니다(lastKeepBalanceTS는 가장 최근의 밸런싱 작업의 타임스탬프를 표시하는 데 사용됩니다. 0으로 설정하면 밸런싱 작업이 즉시 실행됩니다). 그런 다음 보류 중인 모든 주문을 취소합니다.

  • 균형 잡기
        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 
                }                
            }            
        }

균형기능이 정상적으로 실행되는 것을 확인할 수 있으나, 헤지동작이 트리거되면,lastKeepBalanceTS0으로 재설정하면 균형 조정 작업이 즉시 실행됩니다. 잔액이 성공적으로 계산되면 수익이 계산됩니다.

  • 상태 표시줄 정보
        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)

상태 표시줄은 디자인이 특별히 복잡하지 않습니다. 현재 시간, 거래소 A에서 거래소 B까지의 가격 차이, 거래소 B에서 거래소 A까지의 가격 차이를 표시합니다. 현재의 헤지 대상 스프레드를 표시합니다. A거래소 계좌와 B 거래소 계좌의 자산 데이터를 표시합니다.

다양한 통화로 거래되는 쌍의 처리

매개변수 측면에서 우리는 전략 시작 시 전환율 값 매개변수를 설계했습니다.main또한 우리는 함수의 초기 작동을 위해 환율 변환을 설계했습니다. 다음 사항을 유의하시기 바랍니다.SetRate먼저 환율 변환 기능을 실행해야 합니다. 이 기능은 두 가지 수준에 영향을 미칩니다.

  • 모든 시장 데이터, 주문 데이터, 포지션 데이터의 가격 변환.
  • 계정 자산의 표시 통화를 변환합니다. 예를 들어, 현재 거래 쌍은 다음과 같습니다.BTC_USDT, 가격 단위는 다음과 같습니다.USDT계정 자산에서 사용 가능한 통화는 다음과 같습니다.USDT. CNY로 변환하고 싶다면 코드에 설정하세요.exchange.SetRate(6.8)단지exchange이 교환 대상 아래의 모든 기능에서 얻은 데이터는 CNY로 변환됩니다. 환전에 통화를 사용하는 이유는 무엇입니까?SetRate함수 전달현재 통화에서 목표 통화로의 환율

전체 전략:다양한 통화에 대한 스팟 헤지 전략(튜토리얼)