Analyse de la stratégie de l'exploitant de la récolte (2)

Auteur:Je suis désolée., Créé: 2022-04-26 15:57:02, Mis à jour: 2022-04-26 15:57:53

Analyse de la stratégie de l'exploitant de la récolte (2)

Continuons avec lecontenu de la dernière foisPour expliquer.

Troisième fonction ajoutée:

    self.balanceAccount = function() {
        var account = exchange.GetAccount()
        if (!account) {
            return
        }
        self.account = account
        var now = new Date().getTime()
        if (self.orderBook.Bids.length > 0 && now - self.preCalc > (CalcNetInterval * 1000)) {
            self.preCalc = now
            var net = _N(account.Balance + account.FrozenBalance + self.orderBook.Bids[0].Price * (account.Stocks + account.FrozenStocks))
            if (net != self.preNet) {
                self.preNet = net
                LogProfit(net)
            }
        }
        self.btc = account.Stocks
        self.cny = account.Balance
        self.p = self.btc * self.prices[self.prices.length-1] / (self.btc * self.prices[self.prices.length-1] + self.cny)
        var balanced = false
        
        if (self.p < 0.48) {
            Log("start to balance", self.p)
            self.cny -= 300
            if (self.orderBook.Bids.length >0) {
                exchange.Buy(self.orderBook.Bids[0].Price + 0.00, 0.01)
                exchange.Buy(self.orderBook.Bids[0].Price + 0.01, 0.01)
                exchange.Buy(self.orderBook.Bids[0].Price + 0.02, 0.01)
            }
        } else if (self.p > 0.52) {
            Log("start to balance", self.p)
            self.btc -= 0.03
            if (self.orderBook.Asks.length >0) {
                exchange.Sell(self.orderBook.Asks[0].Price - 0.00, 0.01)
                exchange.Sell(self.orderBook.Asks[0].Price - 0.01, 0.01)
                exchange.Sell(self.orderBook.Asks[0].Price - 0.02, 0.01)
            }
        }
        Sleep(BalanceTimeout)
        var orders = exchange.GetOrders()
        if (orders) {
            for (var i = 0; i < orders.length; i++) {
                if (orders[i].Id != self.tradeOrderId) {
                    exchange.CancelOrder(orders[i].Id)
                }
            }
        }
    }

Lorsque le constructeurLeeksReaper()est en train de construire un objet, lebalanceAccount()La fonction ajoutée à l'objet est utilisée pour mettre à jour les informations sur les actifs du compte, qui sont stockées dansself.account, c'est à dire construire l'attributaccountLa valeur de retour est calculée et imprimée régulièrement. Ensuite, selon les dernières informations sur les actifs du compte, le ratio de solde des symboles de devises au comptant (solde de position au comptant) est calculé, et lorsque le seuil de décalage est déclenché, de petits ordres sont fermés pour ramener les symboles (positions) à un état d'équilibre. Attendez un certain temps pour exécuter le commerce, puis annulez tous les ordres en attente, et exécutez la fonction dans le tour suivant, le solde sera détecté à nouveau et le traitement correspondant sera effectué.

Examinons le code de cette instruction de fonction instruction par instruction: Tout d'abord, la première déclarationvar account = exchange.GetAccount()déclare une variable localeaccount, appelle leexchange.GetAccount()fonction dans l'interface FMZ API, obtenir les dernières données du compte courant et l'assigner à la variableaccountAlors, jugez la variableaccount; si la valeur de la variable est:null(ce qui se produira lorsqu'il ne parvient pas à obtenir la variable, telle que le temps d'arrêt, le réseau, l'exception de l'interface de la plateforme, etc.), il retournera directement (correspondant àif (!account ){...}ici).

La déclarationself.account = accountest d'attribuer la variable localeaccountà l'attributaccountde l'objet construit pour enregistrer les dernières informations de compte dans l'objet construit.

La déclarationvar now = new Date().getTime()déclare une variable localenow, et appelle legetTime()fonction de l'objet temps et date du langage JavaScript pour retourner l'horodatage actuel, et attribuer l'horodatage à la variablenow.

Le code:if (self.orderBook.Bids.length > 0 && now - self.preCalc > (CalcNetInterval * 1000)) {...}juge la différence entre l'horodatage actuel et le dernier enregistré; si la valeur dépasse le paramètreCalcNetInterval * 1000, cela signifie qu'il a dépasséCalcNetInterval * 1000en millisecondes (CalcNetIntervalLes prix de l'achat 1 sur le marché doivent être utilisés pour calculer le bénéfice, la condition est également limitée à la condition de l'achat 1 sur le marché.self.orderBook.Bids.length > 0(données de profondeur, qui doivent être valides dans la liste des ordres d'achat en tant qu'informations de niveau).

Lorsque la condition de l'instruction if est déclenchée, exécuterself.preCalc = nowpour mettre à jour la variable timestampself.preCalcdu dernier bénéfice imprimé à l'horodatage en coursnowIci, les statistiques sur les bénéfices utilisent la méthode de calcul de la valeur nette, le code est:var net = _N(account.Balance + account.FrozenBalance + self.orderBook.Bids[0].Price * (account.Stocks + account.FrozenStocks)), c'est-à-dire convertir la devise en actif (monnaie de cotation) selon le prix d'achat 1 en cours, puis la additionner au montant de l'actif sur le compte et l'assigner à la variable locale déclaréenet. Déterminez si la valeur nette totale actuelle est compatible avec la dernière valeur nette totale enregistrée:

            if (net != self.preNet) {
                self.preNet = net
                LogProfit(net)
            }

En cas d'incohérence, par exemplenet != self.preNetest vrai, mettre à jour l'attributself.preNetqui enregistre la valeurnetEnsuite, imprimez les données de valeur nette totalenetà la courbe de profit du graphique du bot de la plateforme de trading FMZ Quant (vous pouvez interroger leLogProfitfonction dans la documentation de l'API FMZ).

Si l'impression régulière de la déclaration n'est pas déclenchée, continuez le processus suivant: enregistreraccount.Stocks(les symboles monétaires actuellement disponibles sur le compte) etaccount.Balance(actifs disponibles en cours sur le compte) enself.btcetself.cny. Calculer le rapport de décalage et l'attribuer, qui est enregistré dansself.p.

self.p = self.btc * self.prices[self.prices.length-1] / (self.btc * self.prices[self.prices.length-1] + self.cny)

L'algorithme est également très simple, qui consiste à calculer le pourcentage de la valeur en monnaie courante dans la valeur nette totale du compte.

Alors, comment jugez-vous quand le solde de la monnaie (position) est déclenché? Ici, le développeur utilise 50% en hausse et en baisse 2 points de pourcentage comme tampon; si elle dépasse le tampon, exécuter le solde, c'est-à-dire lorsqueself.p < 0.48Si vous pensez que le montant de la devise est faible, chaque fois que le prix augmente de 0,01, placez trois petits ordres. De même si le solde de la devise estself.p > 0.52, si vous pensez que le montant de la devise est grande, pend petites commandes de vendre 1 prix sur le marché. enfin, attendre une certaine période de temps, selon les paramètresSleep(BalanceTimeout), et annuler toutes les commandes.

        var orders = exchange.GetOrders()                  # obtain all the current pending orders, and save them in the variable orders"
        if (orders) {                                      # if the variable "orders", which obtains all the current pending orders, is not null
            for (var i = 0; i < orders.length; i++) {      # use the loop to traverse "orders", and cancel the orders one by one 
                if (orders[i].Id != self.tradeOrderId) {
                    exchange.CancelOrder(orders[i].Id)     # call "exchange.CancelOrder", and cancel orders by "orders[i].Id"
                }
            }
        }

Quatrième fonction ajoutée:

C'est ici que se trouve la partie centrale de la stratégie, le point culminant.self.poll = function() {...}La fonction est la logique principale de l'ensemble de la stratégie.main( )fonction, commencer à exécuter; avant d'entrer dans lewhileboucle infinie, nous utilisonsvar reaper = LeeksReaper()pour construire l'objet de la récolte de profit, puisReaper.poll()est appelé cycliquement dans lemain() function.

Leself.pollla fonction commence à s'exécuter et fait quelques préparatifs avant chaque boucle;self.numTick++augmente le nombre;self.updateTrades()met à jour les dossiers de négociation récents sur le marché et calcule les données correspondantes utilisées;self.updateOrderBook()met à jour les données du marché (carnet de commandes) et calcule les données pertinentes;self.balanceAccount()vérifie le solde monétaire (position).

        var burstPrice = self.prices[self.prices.length-1] * BurstThresholdPct   # calculate the burst price 
        var bull = false             # declare the variable marked by the bull market; the initial value is false
        var bear = false             # declare the variable marked by the bear market; the initial value is false
        var tradeAmount = 0          # declare the variable of trading amount; the initial value is 0

Ensuite, nous devons juger si le marché à court terme actuel est un taureau ou un ours.

        if (self.numTick > 2 && (
            self.prices[self.prices.length-1] - _.max(self.prices.slice(-6, -1)) > burstPrice ||
            self.prices[self.prices.length-1] - _.max(self.prices.slice(-6, -2)) > burstPrice && self.prices[self.prices.length-1] > self.prices[self.prices.length-2]
            )) {
            bull = true
            tradeAmount = self.cny / self.bidPrice * 0.99
        } else if (self.numTick > 2 && (
            self.prices[self.prices.length-1] - _.min(self.prices.slice(-6, -1)) < -burstPrice ||
            self.prices[self.prices.length-1] - _.min(self.prices.slice(-6, -2)) < -burstPrice && self.prices[self.prices.length-1] < self.prices[self.prices.length-2]
            )) {
            bear = true
            tradeAmount = self.btc
        }

Vous vous souvenez duself.updateOrderBook()fonction dans l'article précédent, dans lequel nous avons utilisé l'algorithme de moyenne pondérée pour construire une série temporellepricesCette pièce de code utilise trois nouvelles fonctions, à savoir_.min, _.max, slice, qui sont également très faciles à comprendre.

  • _.min: La fonction consiste à trouver le minimum dans le tableau de paramètres.

  • _.max: La fonction consiste à trouver le maximum dans le tableau de paramètres.

  • slice: Cette fonction est une fonction membre de l'objet de tableau JavaScript. Elle est d'intercepter et de retourner une partie du tableau selon l'index. Par exemple:

    function main() {
        // index     .. -8 -7 -6 -5 -4 -3 -2 -1
        var arr = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
        Log(arr.slice(-5, -1))    // it will intercept several elements from 4 to 1, and return a new array: [4,3,2,1]
    }
    

Ici, les conditions pour juger s'il s'agit d'un marché haussier ou d'un marché baissier sont:

  • self.numTick > 2doit être vraie, c'est-à-dire que si l'éclatement de prix se produit lors d'un nouveau cycle de détection, il doit être déclenché après au moins trois cycles de détection et éviter de déclencher au début.
  • Les dernières données de la série de prixself.prices, c'est-à-dire la différence entre les données les plus récentes et le prix maximal ou minimum dans leself.pricesl'ensemble dans la plage précédente devrait briser leburstPrice .

Si toutes les conditions sont vraies, marquerbulloubearcommetrue, et attribuer une valeur à la variabletradeAmount, et planifier un échange de cheval.

Ensuite, pour le paramètreBurstThresholdVol, sur la base de laself.volLes résultats de l'enquête ont été mis à jour et calculés au cours de l'année précédente.self.updateTrades()La décision de réduire l'intensité de négociation (réduire le volume de négociation prévu).

        if (self.vol < BurstThresholdVol) {
            tradeAmount *= self.vol / BurstThresholdVol   // reduce the planned trading volume, and reduce it to the previous volume multiplied by "self.vol / BurstThresholdVol" 
        }
        
        if (self.numTick < 5) {
            tradeAmount *= 0.8      // reduced to 80% of the plan 
        }
        
        if (self.numTick < 10) {    // reduced to 80% of the plan
            tradeAmount *= 0.8
        }

Ensuite, jugez si le signal de négociation et le volume de négociation répondent aux exigences:

        if ((!bull && !bear) || tradeAmount < MinStock) {   # if it is not a bull market nor a bear market, or the planned trading volume "tradeAmount" is less than the minimum trading volume "MinStock" set by the parameter, the "poll" function returns directly without any trading operation
            return
        }

Après le jugement susmentionné, exécutervar tradePrice = bull ? self.bidPrice : self.askPriceSelon qu'il s'agit d'un marché baissier ou d'un marché haussier, définir le prix de négociation et attribuer la valeur au prix de l'ordre de livraison correspondant.

Enfin, entrez unewhilela boucle; la seule condition d'arrêt et de rupture de la boucle esttradeAmount >= MinStock, c'est-à-dire que le volume de négociation prévu est inférieur au volume de négociation minimum. Dans la boucle, selon l'état actuel du marché haussier ou l'état du marché baissier, exécuter l'ordre.orderIdJe vais l' exécuter.Sleep(200)La boucle juge ensuite si une commande a été passée à la suite d'une commande.orderIdest vrai (si la commande échoue, l'ID de commande ne sera pas renvoyé, et la condition if ne sera pas déclenchée). Si la condition est vraie, obtenir l'ID de commande et l'assigner àself.tradeOrderId.

Déclarer une variableorderpour stocker les données de commande avec la valeur initiale denull. Ensuite, utilisez une boucle pour obtenir les données d'ordre avec l'ID et déterminez si l'ordre est dans l'état d'ordre en attente; s'il est dans l'état d'ordre en attente, annulez l'ordre avec l'ID; s'il n'est pas dans l'état d'ordre en attente, il sortira de la boucle de détection.

                var order = null           // declare a variable to save the order data 
                while (true) {             // a while loop 
                    order = exchange.GetOrder(orderId)    // call "GetOrder" to query the order data with the ID of  orderId
                    if (order) {                          // if the order data is queried,and the query fails, the order is null, and "if" will not be triggered  
                        if (order.Status == ORDER_STATE_PENDING) {   // judge whether the current order status is pending order
                            exchange.CancelOrder(orderId)            // if the current order status is pending order, cancel the order 
                            Sleep(200)
                        } else {                                     // if not, execute "break" to break out of the while loop 
                            break
                        }
                    }
                }

Ensuite, procédez comme suit:

                self.tradeOrderId = 0              // reset "self.tradeOrderId"
                tradeAmount -= order.DealAmount    // update "tradeAmount", and subtract the executed amount of the orders in the delivery order 
                tradeAmount *= 0.9                 // reduce the intensity of ordering  
                if (order.Status == ORDER_STATE_CANCELED) {     // if the order is canceled 
                    self.updateOrderBook()                      // update the data, including the order book data
                    while (bull && self.bidPrice - tradePrice > 0.1) {   // in a bull market, if the updated bid price exceeds the current trading price by 0.1, reduce the trading intensity, and slightly adjust the trading price 
                        tradeAmount *= 0.99
                        tradePrice += 0.1
                    }
                    while (bear && self.askPrice - tradePrice < -0.1) {  // in a bear market, if the updated ask price exceeds the current trading price by 0.1, reduce the trading intensity, and slightly adjust the trading price 
                        tradePrice -= 0.1
                    }
                }

Lorsque le flux du programme s'éteintwhile (tradeAmount >= MinStock) {...}Le cycle de négociation de l'éclatement des prix est terminé. Exécuterself.numTick = 0, c'est à dire, réinitialiserself.numTickà 0.

La dernière exécution du constructeurLeeksReaper()renvoie leselfl'objet, c'est-à-dire lorsquevar reaper = LeeksReaper(), l' objet est retourné àreaper.

Jusqu'à présent, nous avons analysé comment leLeeksReaper()constructeur construit cet objet récolteur de profit, les différentes méthodes de l'objet, et le processus d'exécution des principales fonctions logiques.


Plus de