Die Ressourcen sind geladen. Beförderung...

Konzeption einer Spot-Hedging-Strategie für Kryptowährungen (1)

Schriftsteller:FMZ~Lydia, Erstellt: 2022-08-16 10:30:56, aktualisiert: 2023-09-19 21:46:16

img

Konzeption einer Spot-Hedging-Strategie für Kryptowährungen (1)

Hedging-Strategien sind sehr gute Praxisstrategien für Anfänger in der Strategieentwicklung.

Entwurf einiger Funktionen und Strategie-Schnittstellenparameter gemäß den Strategieanforderungen

Zunächst einmal ist klar, dass die zu entwerfende Strategie eine Kryptowährungs-Spot-Hedging-Strategie ist. Wir entwerfen die einfachste Hedging-Strategie. Wir verkaufen an der Börse mit dem höheren Preis nur zwischen den beiden Spot-Börsen und kaufen an der Börse mit dem niedrigeren Preis, um den Unterschied zu nehmen. Wenn die Börsen mit den höheren Preisen alle Münzen sind (weil die Münzen mit den höheren Preisen verkauft werden), und die Börsen mit den niedrigeren Preisen alle Münzen sind (die Münzen mit den niedrigeren Preisen werden gekauft), kann es nicht abgesichert werden. Zu diesem Zeitpunkt können wir nur warten, bis die Preisumkehr abgesichert ist.

Bei der Absicherung sind Preis und Menge des Auftrags durch den Austausch begrenzt, und es gibt auch eine Grenze für die Mindestbestellmenge. Zusätzlich zu der Mindestgrenze muss die Strategie bei der Absicherung auch das maximale Auftragsvolumen gleichzeitig berücksichtigen. Wenn das Auftragsvolumen zu groß ist, wird es nicht genug Auftragsvolumen geben. Es ist auch notwendig, zu prüfen, wie der Wechselkurs umgerechnet wird, wenn die beiden börsennotierten Münzen unterschiedlich sind. Bei der Absicherung sind die Bearbeitungsgebühr und der Rutsch des Auftragsnehmers alle Transaktionskosten, nicht solange es einen Preisunterschied gibt, der abgesichert werden kann. Daher hat der Absicherungspreisunterschied auch einen Auslöserwert. Wenn er unter einem bestimmten Preisunterschied liegt, verliert die Absicherung.

Auf der Grundlage dieser Überlegungen muss die Strategie mit mehreren Parametern konzipiert werden:

  • Sicherungsdifferenz:hedgeDiffPrice, wenn die Differenz diesen Wert übersteigt, wird die Absicherungsaktion ausgelöst.
  • Mindestsicherungsbetrag:minHedgeAmount, der Mindestbestellbetrag (Münzen), der abgesichert werden kann.
  • Höchstbetrag der Absicherung:maxHedgeAmount, der maximale Auftragsbetrag (Münzen) für eine Absicherung.
  • Preisgenauigkeit von A:pricePrecisionA, die von der Börse A platzierte Orderpreisgenauigkeit (Anzahl der Dezimalstellen).
  • Die Präzision von A:amountPrecisionA, die Höhe der Präzision der Bestellung der Börse A (Anzahl der Dezimalstellen).
  • Preisgenauigkeit von B:pricePrecisionB, die von Börse B platzierte Orderpreisgenauigkeit (Anzahl der Dezimalstellen).
  • Genauigkeit des Betrags B:amountPrecisionB, die Höhe der Präzision der Bestellung der Börse B (Anzahl der Dezimalstellen).
  • Wechselkurs A:rateA, die Wechselkursumrechnung des ersten hinzugefügten Wechselgegenstands, Standardwert 1, nicht umgerechnet.
  • Wechselkurs B:rateB, die Wechselkursumrechnung des zweiten hinzugefügten Tauschgegenstands, Standardwert 1, nicht umgerechnet.

Die Absicherungsstrategie muss die Anzahl der Münzen in den beiden Konten unverändert halten (d. h. keine Positionen in irgendeiner Richtung halten und Neutralität beibehalten), so dass eine Balance-Logik in der Strategie vorhanden sein muss, um immer das Gleichgewicht zu erkennen.

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

Nach der Auftragserteilung, wenn es keine abgeschlossene Bestellung gibt, müssen wir sie rechtzeitig stornieren, und die Bestellung kann nicht ausstehen bleiben.

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

Wenn wir die Anzahl der Münzen ausgleichen, müssen wir den Preis finden, der auf eine bestimmte Anzahl von Münzen in einer bestimmten Tiefe angehäuft ist, also brauchen wir eine solche Funktion, um sie zu handhaben.

  • GetDepthPrice ist ein
    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 Absicherungsorder-Operation entwerfen und schreiben, die für gleichzeitige Aufträge konzipiert werden muss:

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

Abschließend wollen wir das Design der Balance-Funktion, die etwas kompliziert ist, abschließen.

  • die Balance halten
    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 
        }
    }
    

Nach der Gestaltung dieser Funktionen nach den Anforderungen der Strategie, dann beginnen, die Hauptfunktion der Strategie zu entwerfen.

Hauptfunktionsgestaltung der Strategie

Auf der FMZ-Plattform wird die Strategie vonmainIn den letzten Jahren hat sich die Zahl dermainFunktion, müssen wir einige Initialisierung Arbeit der Strategie zu tun.

  • Name des Austauschobjekts Da viele Operationen in der Strategie die Austauschobjekte verwenden müssen, wie z.B. Marktnoten zu erhalten, Aufträge zu platzieren und so weiter.

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

    Das macht es einfacher, später Code zu schreiben.

  • Wechselkurs, Präzisionskonstruktion

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

    Wenn die WechselkursparameterrateA, rateBsind auf 1 festgelegt (Standard ist 1), d. h.rateA != 1oderrateB != 1wird nicht ausgelöst, so dass die Wechselkursumrechnung nicht festgelegt wird.

  • Alle Daten zurücksetzen

    img

    Manchmal ist es notwendig, alle Protokolle zu löschen und die aufgezeichneten Daten zu löschen, wenn die Strategie startet.isReset, und entwerfen Sie den Reset-Code im Initialisierungsteil der Strategie, zum Beispiel:

      if (isReset) {   // When isReset is true, reset the data
          _G(null)
          LogReset(1)
          LogProfitReset()
          LogVacuum()
          Log("reset all data", "#FF0000")
      }
    
  • Wiederherstellung der ursprünglichen Kontodaten, Aktualisierung der Leistungskontodaten Um den Saldo zu beurteilen, muss die Strategie die Ausgangssaldoaktivitäten kontinuierlich zur Vergleichung mit dem aktuellen Konto erfassen.nowAccswird verwendet, um die Leistungsbilanzdaten zu erfassen, mit der Funktion, die wir gerade entworfenupdateAccsum die Konto-Daten der aktuellen Börse zu erhalten.initAccswird verwendet, um den ursprünglichen Kontozustand (Anzahl der Münzen, Anzahl der laufenden Münzen usw. an den Börsen A und B) zu erfassen.initAccs, verwenden_G()Funktion, um zuerst wiederherzustellen (die _G-Funktion wird Daten dauerhaft aufzeichnen, und kann die aufgezeichneten Daten wieder zurückgeben, siehe die API-Dokumentation für Details: [Link](https://www.fmz.com/api#_gk-v)), wenn die Abfrage nicht funktioniert, verwenden Sie die Leistungskontoinformationen, um den Wert zuzuordnen, und verwenden Sie die_GFunktion zum Aufzeichnen.

    Der folgende Code:

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

Handelslogik, Hauptschleife in Hauptfunktion

Der Code in der Hauptschleife ist der Prozess jeder Ausführungsrunde der Strategie-Logik, die immer wieder ausgeführt wird, um die Hauptschleife der Strategie zu bilden.

  • Erhalten von Marktdaten und Beurteilung der Gültigkeit von Marktdaten

          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 können wir sehen, dass die Gleichzeitfunktionexchange.Goder FMZ-Plattform verwendet wird, um gleichzeitige Objekte zu erstellendepthARoutine, depthBRoutineDas nennen dieGetDepth()Wenn diese beiden gleichzeitigen Objekte erstellt werden,GetDepth()Die Interface wird sofort aufgerufen, und beide Anfragen für Tiefendaten werden an den Austausch gesendet. Dann rufen Sie diewait()Verfahren derdepthARoutine, depthBRoutineObjekte zur Erfassung der Tiefendaten.
    Nach Erhalt der Tiefendaten ist es notwendig, die Tiefendaten zu überprüfen, um ihre Gültigkeit zu ermitteln.continueDie Anweisung wird ausgelöst, um die Hauptschleife erneut auszuführen.

  • Verwendenspread valueParameter oder diespread ratioParameter?

          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 eine solche Konstruktion für die Parameter gemacht: die Parameter des FMZ könnenSchauoderversteckenbasierend auf einem Parameter, so können wir einen Parameter zu entscheiden, ob zu verwendenprice spread, oderspread ratio.

    img

    Ein ParameterdiffAsPercentageDie beiden anderen Parameter-Einstellungen, die anhand dieses Parameters angezeigt oder verschwiegen werden sollen, sind:hedgeDiffPrice@!diffAsPercentage, die angezeigt wird, wenndiffAsPercentageist falsch.hedgeDiffPercentage@diffAsPercentage, die angezeigt wird, wenndiffAsPercentageDas ist wahr. Nach diesem Entwurf haben wir diediffAsPercentageBei der Ermittlung der Risikopositionsquote wird die Risikopositionsquote in einem bestimmten Wert berechnet.diffAsPercentageDer Wert des Wertpapiers, der in einem anderen Wertpapier verbucht wird, wird durch die Preisdifferenz ausgelöst.

  • Bestimmung der Absicherungsauslöser

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

    Die Auslöserbedingungen für die Absicherung sind wie folgt:

    1. Die Sicherungsspanne muss zuerst erreicht werden und nur dann abgesichert werden können, wenn die Spread des Auftrags die festgelegten Spreadparameter erfüllt.
    2. Der Betrag, der auf dem Markt abgesichert werden kann, muss dem in den Parametern festgelegten Mindestbetrag entsprechen.
    3. Die Vermögenswerte im Austausch der Verkaufsaktion reichen aus, um zu verkaufen, und die Vermögenswerte im Austausch der Kaufsaktion reichen aus, um zu kaufen. Wenn diese Bedingungen erfüllt sind, führen wir die Absicherungsfunktion aus, um eine Absicherungsorder zu platzieren.isTradeHier wird die Variable, wenn die Absicherung ausgelöst wird, auftrue. Und die globale Variable zurücksetzenlastKeepBalanceTSDie Ausgleichszahlung wird auf 0 (lastKeepBalanceTS wird verwendet, um den Zeitstempel der letzten Ausgleichszahlung zu kennzeichnen, wenn sie auf 0 eingestellt wird, wird die Ausgleichszahlung sofort ausgelöst) und dann alle ausstehenden Aufträge storniert.
  • Ausgleichsvorgang

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

    Es kann festgestellt werden, daß die Ausgleichsfunktion periodisch ausgeführt wird, aber wenn dielastKeepBalanceTSDer Gewinn wird nach erfolgreicher Ausgleichsberechnung berechnet.

  • Informationen aus der 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", 
              "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)
    

    Die Statusleiste ist nicht besonders kompliziert gestaltet. Sie zeigt die aktuelle Zeit, den Preisunterschied von Börse A zu Börse B und den Preisunterschied von Börse B zu Börse A an. Und sie zeigt den aktuellen Hedge-Ziel-Spread, die Vermögensdaten des Börsenkontos A und des Börsenkontos B an.

Handel mit Handelspaaren in verschiedenen Währungen

In bezug auf die Parameter haben wir den Wertparameter für den Wechselkurs entworfen, und wir haben auch die Wechselkursumrechnung für den ersten Betrieb desmainDie Strategie wird von der Kommission und den Mitgliedstaaten in Zusammenarbeit mit den Mitgliedstaaten durchgeführt.SetRateDie Wechselkursumrechnung muss zuerst ausgeführt werden. Weil diese Funktion zwei Aspekte betrifft:

  • Preisumrechnung in allen Marktdaten, Auftragsdaten und Positionsdaten.
  • Die Umrechnung der in Währungen lautenden Währung in den Kontovermögen. Zum Beispiel ist das aktuelle HandelspaarBTC_USDT, ist die PreiseinheitUSDT, und die verfügbare Nennwährung in den Kontovermögen ist ebenfallsUSDT. Wenn ich den Wert in CNY umwandeln möchte, setzenexchange.SetRate(6.8)in dem Code, um die Daten zu konvertieren, die von allen Funktionen im Rahmen derexchangeUmtauschobjekt in CNY. Um zu welcher Währung konvertieren, geben Sie inder Wechselkurs von der aktuellen Währung in der ZielwährungDieSetRate function.

Vollständige Strategie:Die Risikopositionen werden von den Risikopositionen gemäß Artikel 42 Absatz 1 Buchstabe c der CRR ausgegliedert.


Verwandt

Mehr