리소스 로딩... 로딩...

암호화폐 스팟 헤지 전략 설계 (1)

저자:FMZ~리디아, 창작: 2022-08-16 10:30:56, 업데이트: 2023-09-19 21:46:16

img

암호화폐 스팟 헤지 전략 설계 (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 the currency difference
        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("unable to balance")            
            } else {
                // balance order
                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("errors:", "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]
    

    이것은 나중에 코드를 작성하는 것을 더 쉽게 만듭니다.

  • 환율, 정확성 관련 설계

      // precision, exchange rate settings
      if (rateA != 1) {
          // set exchange rate A
          exA.SetRate(rateA)
          Log("Exchange A sets the exchange rate:", rateA, "#FF0000")
      }
      if (rateB != 1) {
          // set exchange rate B
          exB.SetRate(rateB)
          Log("Exchange B sets the exchange rate:", rateB, "#FF0000")
      }
      exA.SetPrecision(pricePrecisionA, amountPrecisionA)
      exB.SetPrecision(pricePrecisionB, amountPrecisionB)
    

    만약 환율 매개 변수가rateA, rateB1로 설정됩니다 (디فال트는 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현재 거래소의 계정 데이터를 얻기 위해.initAccsA와 B 거래소에서 초기 계좌 상태 (동전 수, 명화 동전 수 등) 를 기록하는 데 사용됩니다.initAccs, 사용_G()먼저 복원 함수 ( _G 함수는 데이터를 지속적으로 기록하고 기록 된 데이터를 다시 반환 할 수 있습니다, 자세한 내용은 API 문서를 참조하십시오: [링크](https://www.fmz.com/api#_gk-v() 의 경우, 현재 계좌 정보를 사용하여 값을 지정하고_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메인 루프를 다시 실행하기 위해 명령어가 트리거됩니다.

  • 사용spread value파라미터 또는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 conditions are met            
              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("trigger A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, price, amount, nowAccs[1].Balance / price, nowAccs[0].Stocks)  // Tips
                  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 conditions are met
              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("trigger B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, price, amount, nowAccs[0].Balance / price, nowAccs[1].Stocks)  // Tips
                  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", 
              "current A, Stocks:", nowAccs[0].Stocks, "FrozenStocks:", nowAccs[0].FrozenStocks, "Balance:", nowAccs[0].Balance, "FrozenBalance", nowAccs[0].FrozenBalance, "\n", 
              "current B, Stocks:", nowAccs[1].Stocks, "FrozenStocks:", nowAccs[1].FrozenStocks, "Balance:", nowAccs[1].Balance, "FrozenBalance", nowAccs[1].FrozenBalance, "\n", 
              "initial A, Stocks:", initAccs[0].Stocks, "FrozenStocks:", initAccs[0].FrozenStocks, "Balance:", initAccs[0].Balance, "FrozenBalance", initAccs[0].FrozenBalance, "\n", 
              "initial B, Stocks:", initAccs[1].Stocks, "FrozenStocks:", initAccs[1].FrozenStocks, "Balance:", initAccs[1].Balance, "FrozenBalance", initAccs[1].FrozenBalance)
    

    상태 표시줄은 디자인적으로 특별히 복잡하지 않습니다. 현재 시간, A 거래소에서 B 거래소로의 가격 차이 및 A 거래소에서 B 거래소에서 A 거래소로의 가격 차이를 표시합니다. 그리고 현재 헤지 목표 스프레드, A 거래소 계정 및 B 거래소 계정의 자산 데이터를 표시합니다.

각기 다른 화폐로 표기된 거래 쌍의 처리

매개 변수 측면에서, 우리는 환율 가치 매개 변수를 설계했고, 우리는 또한 환율 전환을 설계main이 전략의 시작에서 역할을 합니다.SetRate환율 변환 함수는 먼저 실행되어야 합니다. 왜냐하면 이 기능이 두 가지 측면에 영향을 미치기 때문입니다.

  • 모든 시장 데이터, 주문 데이터 및 위치 데이터의 가격 변환.
  • 계정 자산에 있는 화폐의 환산 예를 들어, 현재 거래 쌍은BTC_USDT, 가격 단위는USDT, 그리고 계좌 자산에 있는 사용 가능한 지폐는 또한USDT만약 제가 CNY로 값을 변환하고 싶다면,exchange.SetRate(6.8)코드에서 모든 함수에서 얻은 데이터를 변환exchangeCNY로 교환하는 목적. 어떤 화폐로 환산하려면현재 통화에서 목표 통화로 환산되는 환율에 대한SetRate function.

전체 전략:다른 통화에 대한 현금 헤지 전략 (교습)


관련

더 많은