avatar of 发明者量化-小小梦 发明者量化-小小梦
konzentrieren Sie sich auf Private Nachricht
4
konzentrieren Sie sich auf
1271
Anhänger

Gestaltung einer Spot-Hedging-Strategie für digitale Währungen (1)

Erstellt in: 2021-07-19 17:38:24, aktualisiert am: 2023-09-20 10:35:16
comments   1
hits   3142

Gestaltung einer Spot-Hedging-Strategie für digitale Währungen (1)

Gestaltung einer Spot-Hedging-Strategie für digitale Währungen (1)

Für Anfänger in der Strategieentwicklung ist die Hedging-Strategie eine sehr gute Trainingsstrategie. Dieser Artikel implementiert eine einfache, aber praxisnahe Spot-Hedging-Strategie für digitale Währungen und hofft, dass Anfänger etwas Designerfahrung sammeln können.

Entwerfen Sie einige Funktionen und Strategieschnittstellenparameter entsprechend den Strategieanforderungen

Zunächst einmal ist klar, dass es sich bei der zu entwickelnden Strategie um eine Spot-Hedging-Strategie für digitale Währungen handelt. Wir entwickeln die einfachste Hedging-Strategie, die darin besteht, an der Börse mit dem höheren Preis zwischen zwei Spot-Börsen zu verkaufen und an der Börse mit dem niedrigeren Preis zu kaufen. um einen Gewinn zu erzielen. Nimm die Differenz. Wenn alle Börsen mit höheren Preisen in Münzen denominiert sind (weil alle Münzen mit höheren Preisen verkauft werden) und alle Börsen mit niedrigeren Preisen in Münzen erfolgen (alle Münzen mit niedrigeren Preisen werden gekauft), ist eine Absicherung unmöglich. Zu diesem Zeitpunkt können Sie nur auf eine Preisumkehr warten und sich absichern.

Beim Hedging unterliegt die Börse Genauigkeitsbeschränkungen hinsichtlich Auftragspreis und -menge, zudem gibt es eine Mindestbestellmengenbeschränkung. Neben der Mindestgrenze muss die Strategie auch das maximale Ordervolumen für die Absicherung auf einmal berücksichtigen. Wenn das Ordervolumen zu groß ist, gibt es nicht genügend Orders auf dem Markt. Sie müssen auch berücksichtigen, wie die Umrechnung anhand des Wechselkurses erfolgt, wenn an den beiden Börsen unterschiedliche Währungen denominiert sind. Beim Hedging sind Bearbeitungsgebühren und Slippage bei Orders beides Transaktionskosten. Solange eine Preisdifferenz besteht, ist ein Hedging nicht möglich. Daher gibt es auch einen Auslösewert für das Hedging der Preisdifferenz. Wenn die Preisdifferenz geringer ist als Ab einem bestimmten Betrag führt die Absicherung zu einem Verlust.

Basierend auf diesen Überlegungen muss die Strategie mehrere Parameter festlegen:

  • Absicherung der Differenz:hedgeDiffPrice, wenn die Preisdifferenz diesen Wert überschreitet, wird die Absicherungsoperation ausgelöst.
  • Mindestabsicherungsbetrag:minHedgeAmount, die Mindestbestellmenge (in Münzen), die abgesichert werden kann.
  • Maximaler Absicherungsbetrag:maxHedgeAmount, die maximale Bestellmenge (Anzahl der Münzen) für einen Hedge.
  • APreisgenauigkeit:pricePrecisionA, die Orderpreisgenauigkeit (Anzahl der Dezimalstellen) der Börse A.
  • Genauigkeit der Bestellmenge:amountPrecisionA, die Bestellmengengenauigkeit (Anzahl der Dezimalstellen) der Börse A.
  • BPreisgenauigkeit:pricePrecisionB, die Orderpreisgenauigkeit (Anzahl der Dezimalstellen) der Börse B.
  • Genauigkeit der Bestellmenge:amountPrecisionB, die Auftragsmengengenauigkeit (Anzahl der Dezimalstellen) der Börse B.
  • Ein Wechselkurs:rateA, die Wechselkursumrechnung des ersten hinzugefügten Umtauschobjekts, der Standardwert ist 1, keine Umrechnung.
  • Wechselkurs der B-Börse:rateB, die Wechselkursumrechnung des zweiten hinzugefügten Umrechnungsobjekts, der Standardwert ist 1 und es wird keine Umrechnung durchgeführt.

Die Absicherungsstrategie muss die Anzahl der Münzen auf den beiden Konten unverändert lassen (d. h., keine Richtungspositionen halten und Neutralität wahren), daher muss in der Strategie eine Ausgleichslogik vorhanden sein, um den Ausgleich immer zu überprüfen. Bei der Überprüfung des Kontostands ist es unumgänglich, Vermögensdaten von zwei Börsen abzurufen. Um es zu verwenden, müssen wir eine Funktion schreiben.

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

Wird eine Bestellung nach der Auftragserteilung nicht ausgeführt, müssen wir diese rechtzeitig stornieren und dürfen sie nicht in der Schwebe lassen. Dieser Vorgang muss sowohl im Bilanzmodul als auch in der Absicherungslogik behandelt werden, daher muss eine vollständige Funktion zur Auftragsrücknahme entwickelt werden.

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

Beim Ausgleichen der Anzahl von Münzen müssen wir den Preis einer bestimmten Anzahl von Münzen ermitteln, die in einer bestimmten Datentiefe angesammelt sind. Daher benötigen wir eine solche Funktion, um dies zu handhaben.

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

Dann müssen wir die spezifische Absicherungsauftragsoperation entwerfen und schreiben, die als gleichzeitige Order konzipiert werden muss:

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

Lassen Sie uns abschließend den Entwurf der Balance-Funktion abschließen, der ein wenig kompliziert ist.

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

Nachdem diese Funktionen nun entsprechend den Strategieanforderungen entworfen wurden, können wir mit der Gestaltung der Hauptfunktion der Strategie beginnen.

Strategie Hauptfunktion Design

Bei FMZ besteht die Strategie darin,mainDie Ausführung der Funktion beginnt. existierenmainZu Beginn der Funktion müssen wir einige Strategieinitialisierungen durchführen.

  • Austauschobjektname Denn viele Operationen in der Strategie erfordern den Einsatz von Tauschobjekten, etwa das Einholen von Marktinformationen, das Platzieren von Aufträgen usw. Es wäre also mühsam, jedes Mal einen langen Namen zu verwenden. Der Trick besteht darin, stattdessen einen einfachen Namen zu verwenden, zum Beispiel:
  var exA = exchanges[0]
  var exB = exchanges[1]

Dies macht das spätere Schreiben von Code sehr komfortabel.

  • Wechselkurs- und genauigkeitsbezogenes Design
    // 精度,汇率设置
    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)

Wenn der WechselkursparameterrateArateBEinige sind auf 1 gesetzt (der Standardwert ist 1), das heißtrateA != 1oderrateB != 1Wird nicht ausgelöst, daher wird keine Wechselkursumrechnung vorgenommen.

  • Alle Daten zurücksetzen

Gestaltung einer Spot-Hedging-Strategie für digitale Währungen (1)

Manchmal ist es beim Starten einer Richtlinie erforderlich, alle Protokolle zu löschen und aufgezeichnete Daten zu entfernen. Sie können einen Strategie-Schnittstellenparameter entwerfenisReset, und entwerfen Sie dann den Reset-Code im Initialisierungsabschnitt der Strategie, zum Beispiel:

    if (isReset) {   // 当isReset为真时重置数据
        _G(null)
        LogReset(1)
        LogProfitReset()
        LogVacuum()
        Log("重置所有数据", "#FF0000")
    }
  • Ursprüngliche Kontodaten wiederherstellen und aktuelle Kontodaten aktualisieren Zur Ermittlung des Saldos ist im Rahmen der Strategie eine kontinuierliche Aufzeichnung des anfänglichen Kontobestands zum Vergleich mit der aktuellen Situation erforderlich.nowAccsMit dieser Variable werden die aktuellen Kontodaten mithilfe der soeben entworfenen Funktion aufgezeichnet.updateAccsHolen Sie sich die Kontodaten der aktuellen Börse.initAccsWird verwendet, um den anfänglichen Kontostatus aufzuzeichnen (Daten wie die Anzahl der Münzen und die Anzahl der denominierten Münzen von Börse A und Börse B). fürinitAccsErster Gebrauch_G()Funktionswiederherstellung (_Die G-Funktion zeichnet Daten dauerhaft auf und kann die aufgezeichneten Daten erneut zurückgeben. Einzelheiten finden Sie in der API-Dokumentation:Link), wenn die Abfrage fehlschlägt, verwenden Sie die aktuellen Kontoinformationen, um einen Wert zuzuweisen und verwenden Sie_GFunktionsaufzeichnung.

Beispielsweise der folgende Code:

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

Handelslogik, Hauptschleife in der Hauptfunktion

Der Code in der Hauptschleife ist der Prozess jeder Ausführungsrunde der Strategielogik. Die kontinuierliche wechselseitige Ausführung bildet die Hauptschleife der Strategie. Schauen wir uns den Ablauf jeder Programmausführung in der Hauptschleife an.

  • Marktdaten beschaffen und deren Validität bestimmen
        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 
        }

Hier sehen Sie die parallelen Funktionen der FMZ-Plattform.exchange.Go, erstellte den AnrufGetDepth()Concurrency-Objekt der SchnittstelledepthARoutinedepthBRoutine. Wenn diese beiden gleichzeitigen Objekte erstellt werden, rufen Sie aufGetDepth()Auch die Schnittstelle erfolgte umgehend und es wurden zwei Anfragen zur Erhebung der Tiefendaten an die Börse gestellt. Dann rufen SiedepthARoutinedepthBRoutineObjektwait()Methode zum Erhalten von Tiefendaten.
Nach dem Erhalt der Tiefendaten müssen die Tiefendaten überprüft werden, um ihre Gültigkeit zu bestimmen. Ausführung bei Datenanomalien auslösencontinueDie Anweisung führt die Hauptschleife erneut aus.

  • verwenden价差值Parameter oder差价比例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
        }

Wir haben ein solches Design hinsichtlich der Parameter erstellt. Die Parameter von FMZ können auf einem bestimmten Parameter basierenzeigenoderverstecken, sodass wir einen Parameter festlegen können, der entscheidet, ob价格差,Trotzdem差价比例

Gestaltung einer Spot-Hedging-Strategie für digitale Währungen (1)

Den Strategieschnittstellenparametern wurde ein Parameter hinzugefügtdiffAsPercentage. Zwei weitere Parameter, die basierend auf diesem Parameter angezeigt oder ausgeblendet werden, sind wie folgt eingestellt: hedgeDiffPrice@!diffAsPercentage,WanndiffAsPercentageMit „False“ wird dieser Parameter angezeigt. hedgeDiffPercentage@diffAsPercentage,WanndiffAsPercentageTrue, um diesen Parameter anzuzeigen. Nach diesem Entwurf überprüften wirdiffAsPercentageDie Parameter basieren auf dem Preisdifferenzverhältnis als Auslösebedingung für die Absicherung. DeaktivierendiffAsPercentageDer Parameter dient zur Verwendung der Preisdifferenz als Auslösebedingung für die Absicherung.

  • Bestimmen Sie die Auslösebedingungen für die Absicherung
        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 
            }            
        }

Es gibt mehrere Auslösebedingungen für die Absicherung: 1. Erstens: Erfüllen Sie die Absicherungspreisdifferenz. Eine Absicherung kann nur durchgeführt werden, wenn die Preisdifferenz des Marktes die festgelegten Preisdifferenzparameter erfüllt. 2. Das Absicherungsvolumen auf dem Markt muss dem in den Parametern festgelegten Mindestabsicherungsvolumen entsprechen. Da verschiedene Börsen unterschiedliche Mindestauftragsvolumina haben können, sollte das kleinere der beiden gewählt werden. 3. An der Börse sind genügend Vermögenswerte vorhanden, um Verkaufstransaktionen durchführen zu können, und an der Börse sind genügend Vermögenswerte vorhanden, um Kauftransaktionen durchführen zu können. Wenn diese Bedingungen erfüllt sind, wird die Hedging-Funktion ausgeführt, um eine Hedging-Order zu platzieren. Vor der Hauptfunktion haben wir vorab eine Variable deklariertisTradeWird verwendet, um zu markieren, ob eine Absicherung erfolgt. Wenn eine Absicherung ausgelöst wird, wird diese Variable auftrue. Und setzen Sie die globalen Variablen zurücklastKeepBalanceTSSetzen Sie lastKeepBalanceTS auf 0 (lastKeepBalanceTS wird verwendet, um den Zeitstempel des letzten Ausgleichsvorgangs zu markieren. Wenn Sie es auf 0 setzen, wird der Ausgleichsvorgang sofort ausgelöst) und stornieren Sie dann alle ausstehenden Aufträge.

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

Sie können sehen, dass die Balance-Funktion regelmäßig ausgeführt wird, aber wenn die Hedging-Operation ausgelöst wird,lastKeepBalanceTSBeim Zurücksetzen auf 0 wird der Ausgleichsvorgang sofort ausgelöst. Nach erfolgreichem Ausgleich wird der Gewinn ermittelt.

  • Informationen zur Statusleiste
        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)

Die Statusleiste ist nicht besonders komplex gestaltet. Sie zeigt die aktuelle Uhrzeit, die Preisdifferenz von Börse A zu Börse B und die Preisdifferenz von Börse B zu Börse A an. Zeigt den aktuellen Hedge-Ziel-Spread an. Zeigt die Vermögensdaten des A-Börsenkontos und des B-Börsenkontos.

Abwicklung von Handelspaaren mit unterschiedlichen denominierten Währungen

Was die Parameter betrifft, haben wir zu Beginn der Strategie den Conversion-Rate-Wertparameter entworfenmainFür die Erstinbetriebnahme der Funktion haben wir zudem eine Wechselkursumrechnung konzipiert. Es ist zu beachten, dassSetRateZuerst muss die Funktion zur Wechselkursumrechnung ausgeführt werden. Denn diese Funktion wirkt auf zwei Ebenen:

  • Preisumrechnung in allen Marktdaten, Orderdaten und Positionsdaten.
  • Umrechnung der Nennwährung in Kontovermögen. Das aktuelle Handelspaar ist beispielsweiseBTC_USDT, die Preiseinheiten sindUSDTDie verfügbare Währung im Kontovermögen istUSDT. Wenn ich es in CNY umrechnen möchte, lege es im Code festexchange.SetRate(6.8)NurexchangeDie von allen Funktionen unter diesem Austauschobjekt erhaltenen Daten werden in CNY konvertiert. Warum wird die Währung zur Umrechnung verwendet?SetRateFunktionsübergabeDer Wechselkurs von der aktuellen Währung zur Zielwährung

Die komplette Strategie:Spot-Hedging-Strategien für verschiedene Währungen (Tutorial)