암호화폐 현금 헤지 전략 (1)

저자:니나바다스, 창작: 2022-04-14 11:57:46, 업데이트: 2022-04-14 16:32:56

암호화폐 현금 헤지 전략 (1)

전략 설계의 초보자에게는 헤지 전략은 연습에 매우 좋습니다. 이 기사는 초보자가 디자인 경험을 배울 수 있도록 희망하여 간단하지만 탄탄한 암호화폐 스팟 헤지 전략을 구현합니다.

전략 요구 사항에 따라 설계 기능 및 매개 변수

우선, 설계될 전략이 암호화폐 스팟 헤지 전략인지 확인해야 합니다. 우리는 가장 간단한 헤지를 설계합니다. 우리는 두 스팟 플랫폼 사이의 더 높은 가격으로 플랫폼에서 판매하고, 가격 스프레드를 벌기 위해 더 낮은 가격으로 플랫폼에서 구매합니다. 더 높은 가격으로 플랫폼이 코트 화폐 기호로 가득했을 때 (값이 높기 때문에 모든 화폐 기호가 판매됩니다), 또는 더 낮은 가격으로 플랫폼이 화폐 기호로 가득했을 때 (값이 낮기 때문에 모든 자산이 통화 기호를 구입하기 때문에), 헤지 할 수 없습니다. 이 시점에서, 당신은 단지 가격이 헤지하기 위해 역전될 때까지 기다릴 수 있습니다.

헤지 과정에서 주문 가격과 금액에 대해서는 모든 플랫폼에 정밀 한계가 있으며, 최소 주문 금액에도 한계가 있습니다. 최소 한계 외에도 전략은 헤지에 대한 최대 주문 금액을 고려해야 합니다. 주문 금액이 너무 크다면 시장은 그것에 대한 충분한 주문 부피가 없을 것입니다. 두 플랫폼이 다른 코트 통화를 가지고 있다면 환율을 변환하는 방법을 고려하는 것도 필요합니다. 헤지 도중 처리 수수료와 주문 입자의 미끄러짐은 모두 거래 비용입니다. 가격 차이가있는 한 헤지는 항상 일어나지 않습니다. 따라서 헤지 가격 스프레드는 트리거 값도 있습니다. 특정 가격 스프레드보다 낮다면 헤지는 손실을 초래합니다.

이를 바탕으로 전략은 여러 매개 변수를 고려하여 설계되어야 합니다.

  • 헤지 스프레드:hedgeDiffPrice: 스프레드가 그 값을 초과하면 헤지가 실행됩니다.
  • 최소 헤지 금액:minHedgeAmount, 헤지에 사용할 수 있는 최소한의 주문 금액 (상호 금액).
  • 최대 헤지 금액:maxHedgeAmount, 헤지에 사용할 수 있는 최대 주문 금액 (상호 금액).
  • 가격 정확성 A:pricePrecisionA, A 플랫폼의 주문 가격 정확도 (입수 소수)
  • 주문 금액 정확성 A:amountPrecisionA, A 플랫폼의 주문 금액 정확도 (입수 소수)
  • 가격 정확성 B:pricePrecisionB, B 플랫폼의 주문 가격 정확도 (입수 소수)
  • 주문 금액 정확성 B:amountPrecisionB, B 플랫폼의 주문 금액 정확도 (입수 소수)
  • 환율 A:rateA, 첫 번째 추가된 교환 대상의 환율; 기본값은 1이므로 환산하지 않습니다.
  • 환율 B:rateB, 두 번째 추가된 교환 대상의 환율; 기본값은 1이므로 환산하지 않습니다.

헤지 전략은 두 계좌의 통화 기호 금액을 변경하지 않고 (즉, 방향 지위를 보유하지 않고 중립을 유지) 유지해야합니다. 따라서 항상 균형을 감지하기 위해 전략에 균형 논리가 있어야합니다. 균형을 확인 할 때 두 플랫폼에서 자산 데이터를 얻는 것이 불가피합니다. 따라서, 우리는 사용하기 위해 함수를 작성해야합니다.

  • 업데이트Accs
    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 
    }
    

주문을 지은 후, 실행된 주문이 없다면, 우리는 그것을 제 시간에 취소해야 하며, 주문은 미뤄질 수 없습니다. 이 작업은 균형 모듈과 헤지 논리 모두에서 처리되어야하므로 모든 주문을 취소하는 기능을 설계하는 것도 필요합니다.

  • 모두 취소
    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
    }
    

그 다음 우리는 특정 헤지 오더 작업을 설계하고 작성해야 합니다. 동시에 오더를 배치하도록 설계되어야 합니다.

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

마지막으로, 조금 더 복잡한 균형 함수의 디자인을 완성해 보겠습니다.

  • 유지합니다
    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 
        }
    }
    

이 기능들은 전략의 요구에 따라 설계되었습니다. 그리고 우리는 전략의 주요 기능을 설계하기 시작할 수 있습니다.

전략 주요 기능 설계

FMZ에서 전략은main기능.main함수, 우리는 전략의 초기화를 해야 합니다.

  • 교환 객체 이름 전략의 많은 작업에 대 한 교환 객체를 사용 하는, 시장 코팅을 얻는, 주문을 배치 하는 등, 그래서 더 긴 이름을 사용 하는 것이 불편 할 수 있습니다, 내 작은 트릭은 대신 간단한 짧은 이름을 사용 하는 것입니다, 예를 들어:

    var exA = exchanges[0]
    var exB = exchanges[1]
    

    그러면 나중에 코드를 작성하는 것이 더 편리합니다.

  • 환율 및 정확성

      // 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)
    

    환율 매개 변수 중 하나인rateA그리고rateB, 1로 설정됩니다 (예정값은 1), 즉,rateA != 1또는rateB != 1그 값은 발동되지 않았으며 환율은 전환할 수 없습니다.

  • 모든 날짜를 다시 설정

    img

    때로는, 모든 로그를 삭제하고 전략 시작될 때 데이터 레코드를 비누를 쓸 필요가 있습니다. 당신은 전략 인터페이스 매개 변수를 설계할 수 있습니다isReset, 그리고 전략의 초기화 부분에서 리셋 코드를 설계합니다. 예를 들어:

      if (isReset) {   // when "isReset" is true, reset the data 
          _G(null)
          LogReset(1)
          LogProfitReset()
          LogVacuum()
          Log("Reset all data", "#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 
          }
    

    여기서 동시 함수가exchange.GoFMZ 플랫폼의 동시 객체를 생성하는 데 사용됩니다depthARoutine그리고depthBRoutine라고 부르는GetDepth()이 두 개의 동시다발적인 객체가 생성되면,GetDepth()인터페이스가 즉시 호출되고 두 개의 깊이 데이터 요청이 플랫폼에 전송됩니다. 그럼 전화해wait()객체의 방법depthARoutine그리고 물체depthBRoutine깊이 데이터를 얻기 위해서요. 깊이 데이터를 얻은 후, 그 유효성을 판단하기 위해 깊이 데이터를 확인해야합니다.continue메인 루프를 다시 실행하기 위해 명령어가 트리거됩니다.

  • 사용price spread또는spread 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
          }
    

    매개 변수 측면에서, 우리는 그런 디자인을 만들었습니다. FMZ의 매개 변수는또는숨겨라매개 변수를 기준으로, 그래서 우리는 사용 여부를 결정하는 매개 변수를 만들 수price spread, 또는spread ratio.

    img

    매개 변수diffAsPercentage전략 인터페이스의 매개 변수에 추가되었습니다. 매개 변수에 따라 표시 또는 숨기는 다른 두 매개 변수는 다음과 같이 설정됩니다.hedgeDiffPrice@!diffAsPercentage■ 언제diffAsPercentage거짓이라면 밝혀질 것입니다.hedgeDiffPercentage@diffAsPercentage■ 언제diffAsPercentage사실이라면, 그것은 표시될 것입니다.
    설계 후, 우리는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 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 
              }            
          }
    

    헤딩을 위한 몇 가지 트리거 조건이 있습니다. 1.첫째, 헤지 스프레드를 충족하십시오. 시장 스프레드가 설정된 스프레드 매개 변수를 충족 할 때만 헤지가 가능합니다.

    2.시장의 헤지 금액은 매개 변수에서 설정된 최소 헤지 금액을 충족해야 합니다.

    3.판매 작업과 함께 플랫폼의 자산은 판매하기에 충분하며 구매 작업과 함께 플랫폼의 자산은 구매하기에 충분합니다. 이러한 조건이 충족되면 헤지 기능을 실행하여 헤지에 따라 주문을합니다. 주요 기능 전에 변수를 선언합니다.isTrade미리 헤지 발생 여부를 표시합니다. 여기, 헤지 트리거되면 변수는true그리고 글로벌 변수를 다시 설정합니다.lastKeepBalanceTS0 (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 
                  }                
              }            
          }
    

    균형 함수가 주기적으로 실행된다는 것을 볼 수 있습니다.lastKeepBalanceTS헤지 작업이 시작되면 0으로 다시 설정되면, 균형 작업이 즉시 시작됩니다. 균형이 성공하면 수익이 계산됩니다.

  • 상태 표시줄 정보

          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)
    

    상태 표시줄은 특별히 복잡하게 설계되지 않았습니다. 현재 시간, 플랫폼 A에서 플랫폼 B의 가격 스프레드와 B에서 A의 가격 스프레드를 표시합니다. 또한 현재 헤지 목표 스프레드, 플랫폼 A의 계정 자산 데이터 및 플랫폼 B의 계정 자산 데이터를 표시합니다.

다른 코팅 화폐에 대한 거래 쌍 처리

매개 변수 측면에서, 우리는 환율 가치 변환의 매개 변수를 설계했고, 우리는 또한main이 전략의 시작에서 역할을 합니다.SetRate환율 변환 함수는 먼저 실행되어야 합니다.

이 기능은 두 가지 측면에 영향을 줄 것입니다:

  • 모든 시장 코팅 데이터, 주문 데이터 및 위치 데이터의 가격 변환.
  • 코팅 화폐를 계좌 자산으로 전환

예를 들어, 현재 거래 쌍은BTC_USDT, 가격 단위는USDT, 그리고 계좌 자산에 있는 사용 가능한 코팅 화폐는 또한USDT만약 제가 자산의 가치를 CNY로 변환하고 싶다면,exchange.SetRate(6.8)코드에서 모든 함수에서 얻은 데이터를 변환exchangeCNY로 전환합니다. 어떤 코트 통화로 변환하려면, 수입현재 코팅 통화에서 목표 코팅 통화로 환율SetRate function.

전체 전략:다른 코팅 화폐의 스팟 헤지 전략 (Teaching)


더 많은