Les ressources ont été chargées... Je charge...

Conception d'une stratégie de couverture de la monnaie numérique (1)

Auteur:L'inventeur de la quantification - un petit rêve, Créé: 2021-07-19 17:38:24, Mis à jour: 2023-09-20 10:35:16

img

Conception d'une stratégie de couverture de la monnaie numérique (1)

Pour les débutants dans la conception de stratégies, les stratégies de couverture sont de très bonnes stratégies d'entraîneur. Cet article met en œuvre une stratégie de couverture instantanée de crypto-monnaie simple mais pratique, dans l'espoir de permettre aux débutants d'apprendre une partie de l'expérience de conception.

Concevoir des fonctions, des paramètres d'interface stratégique en fonction des besoins de la stratégie

Tout d'abord, il est clair que la stratégie à venir est une stratégie de couverture contre les pièces de monnaie numérique, nous avons conçu la couverture la plus simple, en vendant uniquement les pièces les plus chères entre les deux échanges de monnaie, en achetant les pièces les moins chères et en profitant du décalage.

Le prix, la quantité et la précision des commandes sont limitées et il y a une limite minimale. En plus de la limite minimale, les stratégies non limitées de la couverture prennent également en compte la quantité maximale d'un seul ordre de couverture, car il n'y aura pas assez de volume d'ordre si le montant est trop important.

Sur la base de ces considérations, la stratégie doit concevoir plusieurs paramètres:

  • Le déficit de couverture:hedgeDiffPriceLa différence de prix entre les deux prix est de 0,01%.
  • Le dépôt de fonds est limité.minHedgeAmountLe taux de change est le plus bas possible.
  • Le risque maximal:maxHedgeAmountLe taux d'intérêt de la banque est le plus bas.
  • Le prix A est précis:pricePrecisionALa précision des prix des commandes d'un échange A (en minuscules chiffres).
  • A est la précision de l'échantillon:amountPrecisionALa précision de l'échantillon est la plus basse sur A.
  • Le prix B est précis:pricePrecisionBLa précision des prix des commandes sur l'échange B (en minuscules chiffres)
  • La précision de B est la suivante:amountPrecisionBLa précision de l'échantillon B est la précision de l'échantillon B.
  • Le taux d'échange A:rateA, la conversion du taux de change du premier objet d'échange ajouté n'est pas convertie par défaut 1.
  • Le taux de change de l'échange B:rateB, la conversion du taux de change du deuxième objet d'échange ajouté, le 1 par défaut n'est pas converti.

Une stratégie de contrebande nécessite de maintenir le nombre de pièces dans les deux comptes inchangé (c'est-à-dire de ne pas tenir de position dans n'importe quelle direction, de rester neutre) et donc une logique d'équilibre est nécessaire.

  • mise à jourAccs
    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 
    }
    

Si une commande n'est pas passée après l'ordre, nous devons annuler l'ordre en temps opportun, nous ne pouvons pas la laisser pendue. Cette opération doit être traitée dans le module d'équilibrage ou dans la logique de couverture, il est donc nécessaire de concevoir une fonction d'annulation complète de l'ordre.

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

Pour équilibrer le nombre de pièces, nous avons besoin de trouver un prix qui s'accumule à un certain nombre de pièces dans une certaine profondeur de données, donc nous avons besoin d'une fonction comme celle-ci pour traiter.

  • Je vais vous donner le prix.
    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
    }
    

Ensuite, nous avons besoin de concevoir et d'écrire des opérations de sous-ordonnance pour des opérations de sous-ordonnance spécifiques, qui doivent être conçues comme des sous-ordonnances simultanées:

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

Enfin, nous avons terminé la conception de la fonction d'équilibre, qui est un peu plus complexe.

  • maintenir l'équilibre
    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 
        }
    }
    

Les fonctions ont été conçues en fonction des besoins de la stratégie, et vous pouvez commencer à concevoir les principales fonctions de la stratégie ci-dessous.

Conception de la fonction principale

La stratégie sur FMZ est de:mainLa fonction commence à s'exécuter.mainLa partie du début de la fonction où nous allons initialement travailler sur certaines stratégies.

  • Nom de l'objet Comme beaucoup d'opérations de stratégie sont utilisées pour les objets d'échange, tels que l'acquisition d'un marché, la commande, etc. Il est donc gênant d'utiliser un nom plus long à chaque fois, une petite astuce consiste à utiliser un nom plus simple, par exemple:

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

    Il est donc plus facile d'écrire du code par la suite.

  • Des devises, des conceptions de précision

      // 精度,汇率设置
      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)
    

    Si le paramètre de changerateArateBIl y a une valeur de 1 (par défaut 1) qui est:rateA != 1ourateB != 1Le taux de conversion n'est pas activé, donc il n'est pas configuré.

  • Réinitialiser toutes les données

    img

    Il est parfois nécessaire de supprimer tous les journaux et les données de l'enregistrement vide lors du démarrage de la politique.isReset, puis réinitialisez la partie du code de conception dans la stratégie, par exemple:

      if (isReset) {   // 当isReset为真时重置数据
          _G(null)
          LogReset(1)
          LogProfitReset()
          LogVacuum()
          Log("重置所有数据", "#FF0000")
      }
    
  • Récupérer les données du compte initial, mettre à jour les données du compte actuel Pour juger de l'équilibre, la stratégie nécessite de documenter en permanence la situation des actifs du compte initial par rapport à la situation actuelle.nowAccsC'est la variable qui est utilisée pour enregistrer les données des comptes en cours, en utilisant les fonctions que nous venons de concevoir.updateAccsPour obtenir les données de compte de l'échange en cours.initAccsPour enregistrer l'état initial du compte (données telles que le nombre de pièces d'échange A et B, le nombre de pièces de monnaie cotées)initAccsD'abord utilisé_G()Récupération de la fonction ((_G) La fonction _G enregistrera les données de manière permanente et peut récupérer les données enregistrées.Les liensSi vous n'avez pas accès à la requête, attribuez-la et utilisez les informations actuelles de votre compte._GLes fonctions sont enregistrées.

    Par exemple, le code suivant:

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

La logique de transaction, le cercle principal dans une fonction principale

Le code dans la boucle principale est le processus qui est exécuté à chaque tour de la logique stratégique, et les exécutions répétées constituaient la boucle principale.

  • Les données de marché peuvent être utilisées pour évaluer l'efficacité des données de marché.

          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 
          }
    

    Vous pouvez voir ici les fonctions de synchronisation utilisant la plateforme FMZ.exchange.GoNous avons créé un appel.GetDepth()Objets simultanés d'interfacedepthARoutinedepthBRoutineLes deux objets simultanés sont créés en appelantGetDepth()L'interface est également apparue immédiatement, lorsque les deux demandes d'accès à des données profondes ont été envoyées à l'échange. Puis appelle.depthARoutinedepthBRoutinel'objetwait()Les méthodes pour obtenir des données en profondeur.
    Une fois que les données en profondeur ont été obtenues, il est nécessaire de les examiner pour déterminer leur efficacité.continueLes phrases sont réécrites dans le cycle principal.

  • Utilisation价差值Les paramètres sont差价比例Les paramètres?

          var targetDiffPrice = hedgeDiffPrice
          if (diffAsPercentage) {
              targetDiffPrice = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentage
          }
    

    Les paramètres que nous avons conçus sont les suivants: FMZ peut être basé sur un paramètre.AffichageOu alorsIls se cachent.Donc nous pouvons faire un paramètre pour décider si on utilise价格差Je ne sais pas.差价比例

    img

    Un paramètre a été ajouté aux paramètres de l'interface stratégiquediffAsPercentageLes deux autres paramètres affichés ou cachés basés sur ce paramètre sont:hedgeDiffPrice@!diffAsPercentageJe ne sais pasdiffAsPercentageFaux pour afficher ce paramètre.hedgeDiffPercentage@diffAsPercentageJe ne sais pasdiffAsPercentageLe paramètre doit être affiché correctement. C'est ce que nous avons fait.diffAsPercentageParamètres, c'est-à-dire les conditions de déclenchement de la couverture en fonction de la différence de prix.diffAsPercentageLe paramètre est la différence de prix comme condition de déclenchement de la couverture.

  • Déterminez les conditions de déclenchement de la couverture

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

    Les conditions pour déclencher une contrepartie sont les suivantes: 1, la différence de couverture est satisfaite en premier lieu et ne peut être couverte que si la différence de couverture satisfait aux paramètres de différence définis. 2° La valeur de l'endettement doit satisfaire à la valeur minimale de l'endettement définie dans le paramètre, car la valeur minimale de l'endettement peut varier selon les échanges. 3° les actifs de l'échange sur lequel l'opération a été vendue sont suffisamment vendus et les actifs de l'échange sur lequel l'opération a été achetée sont suffisamment achetés; Lorsque ces conditions sont remplies, l'exécution de la fonction de couverture est couverte. Avant la fonction principale, nous déclarons une variable.isTradePour marquer si une couverture est en cours, si la couverture est déclenchée, la variable esttrueEt réinitialisez les variables globales.lastKeepBalanceTSPour 0 ((lastKeepBalanceTS est utilisé pour marquer la barre de temps de la dernière opération de mise en équilibre, la mise à 0 déclenche immédiatement l'opération de mise en équilibre), puis supprimez toutes les listes suspendues.

  • Opération de mise en équilibre

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

    Vous pouvez voir que la fonction d'équilibrage est exécutée régulièrement, mais si une opération de couverture est déclenchée après, la fonction d'équilibrage est déclenchée après.lastKeepBalanceTSUne opération de mise en équilibre est immédiatement déclenchée si elle est réinitialisée à 0.

  • Informations sur le panneau de statut

          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)
    

    La barre d'état n'est pas particulièrement complexe en termes de conception, elle affiche l'heure actuelle, la différence d'un échange A à un échange B et celle d'un échange B à un échange A. Elle affiche la différence d'objectif de couverture actuelle. Elle affiche les données d'actifs des comptes d'un échange A et celles des comptes d'actifs d'un échange B.

Traitement des paires de transactions pour différentes devises

Dans les paramètres, nous avons conçu les paramètres de conversion des taux de change, et nous avons commencé la stratégie.mainNous avons également conçu la conversion des taux de change pour la partie de l'opération initiale de la fonction.SetRateLa fonction de conversion des taux de change doit être exécutée en premier. La fonction fonctionne à deux niveaux:

  • Les prix sont convertis dans toutes les données de marché, les données de commande et les données de stockage.
  • Le taux de change des actifs de l'account. Par exemple, les paires de transactions actuelles sontBTC_USDTLes prix sont les mêmesUSDTLa monnaie utilisée dans les actifs de compte est également la devise.USDTSi je veux convertir une valeur en CNY, je la mets dans le code.exchange.SetRate(6.8)Je vous en prie.exchangeToutes les données obtenues par les fonctions sous cet objet d'échange sont converties en CNY. En échange de quoi les monnaies sont-elles évaluées?SetRateTransfert de fonctionTaux de change de la devise actuelle à la devise cible

La stratégie complète:Les stratégies de couverture des devises en cours d'échange (enseignement)


Relationnée

Plus de

Les écureuils sont des hôtes de l'Ukraine.C'était génial.