
alorsContenu précédentexpliquer.
La 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("开始平衡", 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("开始平衡", 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)
}
}
}
}
ConstructeurLeeksReaper()Lors de la construction d’un objet, ajoutezbalanceAccount()La fonction est de mettre à jour les informations sur les actifs du compte, stockées dansself.account, c’est-à-dire l’objet construitaccountpropriété. Calculez et imprimez régulièrement la valeur du bénéfice. Ensuite, sur la base des dernières informations sur les actifs du compte, le ratio de solde de la devise au comptant (solde de la position au comptant) est calculé. Lorsque le seuil d’écart est atteint, un petit ordre est clôturé pour rétablir l’équilibre de la devise (position). Attendez un certain temps pour terminer la transaction, puis annulez toutes les commandes en attente. Le prochain cycle d’exécution de cette fonction vérifiera à nouveau le solde et effectuera le traitement correspondant.
Regardons le code de cette fonction ligne par ligne :
Première phrasevar account = exchange.GetAccount()Il déclare une variable localeaccount, et appelez l’interface API de l’inventeurexchange.GetAccount()Fonction, obtenir les dernières données du compte courant et les affecter àaccountvariable. Alors jugeaccountCette variable, si la variable estnullSi la valeur (comme le délai d’attente, le réseau, l’exception de l’interface d’échange, etc.) ne parvient pas à être obtenue, elle sera renvoyée directement (correspondant àif (!account){...}ici).
self.account = accountCette phrase sert à mettre la variable localeaccountAttribué à l’objet construitaccountLes attributs sont utilisés pour enregistrer les dernières informations de compte dans l’objet construit.
var now = new Date().getTime()Cette instruction déclare une variable localenowet appelez l’objet heure et date du langage JavaScriptgetTime()La fonction renvoie l’horodatage actuel. Affecter ànowvariable.
if (self.orderBook.Bids.length > 0 && now - self.preCalc > (CalcNetInterval * 1000)) {...}Ce code détermine si la différence entre l’horodatage actuel et le dernier horodatage enregistré dépasse le paramètreCalcNetInterval * 1000Cela signifie que depuis la dernière mise à jour jusqu’à maintenant, plus deCalcNetInterval * 1000milliseconde(CalcNetIntervalsecondes), pour réaliser la fonction de chronométrage de l’impression du revenu. Étant donné que le prix de la première enchère est utilisé lors du calcul du revenu, les conditions limitent égalementself.orderBook.Bids.length > 0Cette condition (données de profondeur, il doit y avoir des informations d’engrenage valides dans la liste de commande d’achat). Lorsque cette condition d’instruction if est déclenchée, l’exécutionself.preCalc = nowMettre à jour la variable d’horodatage des revenus d’impression les plus récentsself.preCalcHorodatage actuelnow. Les statistiques sur les revenus ici utilisent la méthode de calcul de la valeur nette, le code estvar net = _N(account.Balance + account.FrozenBalance + self.orderBook.Bids[0].Price * (account.Stocks + account.FrozenStocks)), c’est-à-dire convertir la pièce en argent (dénominateur) en fonction du prix d’achat actuel, puis l’ajouter au montant d’argent sur le compte et l’affecter à la variable locale déclaréenet. Déterminer si la valeur nette totale actuelle est cohérente avec la valeur nette totale enregistrée la dernière fois :
if (net != self.preNet) {
self.preNet = net
LogProfit(net)
}
S’ils sont incohérents,net != self.preNetSi c’est vrai, utiliseznetLes mises à jour variables sont utilisées pour enregistrer les propriétés de la valeur netteself.preNet. Ensuite, imprimez cecinetDonnées sur la valeur nette totale du graphique de la courbe de profit du robot de la plateforme de trading quantitatif de l’inventeur (peut être interrogé dans le document API FMZ)LogProfitcette fonction).
Si les revenus d’impression planifiés ne sont pas déclenchés, continuez avec le processus suivant.account.Stocks(le nombre de pièces disponibles sur le compte courant),account.Balance(Le montant actuel d’argent disponible sur le compte) est enregistré dansself.btc,self.cny. Calculer le rapport de décalage et attribuer la valeur à l’enregistrementself.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, il consiste à calculer la valeur actuelle de la pièce en pourcentage de la valeur nette totale du compte.
Alors, comment déterminons-nous quand l’équilibre monétaire (position) est déclenché ?
L’auteur utilise 2 points de pourcentage au-dessus et en dessous de 50 % comme tampon et effectue un équilibre au-delà du tampon, c’est-à-dire,self.p < 0.48L’écart du solde des pièces est déclenché et on pense qu’il y a moins de pièces, alors commencez à acheter à une position sur le marché et augmentez le prix de 0,01 à chaque fois, et passez trois petites commandes. De même, la balance des devisesself.p > 0.52, si vous pensez avoir trop de pièces, vous pouvez passer une petite commande en vendant au prix d’ouverture. Enfin, attendez un certain temps en fonction des réglages des paramètresSleep(BalanceTimeout)Toutes les commandes seront annulées par la suite.
var orders = exchange.GetOrders() # 获取当前所有挂单,存在orders变量
if (orders) { # 如果获取当前挂单数据的变量orders不为null
for (var i = 0; i < orders.length; i++) { # 循环遍历orders,逐个取消订单
if (orders[i].Id != self.tradeOrderId) {
exchange.CancelOrder(orders[i].Id) # 调用exchange.CancelOrder,根据orders[i].Id取消订单
}
}
}
La quatrième fonction ajoutée :
La partie centrale de la stratégie, le point culminant est ici,self.poll = function() {...}La fonction est la logique principale de toute stratégie. Nous en avons également parlé dans l’article précédent.main()La fonction commence à s’exécuter et entrewhileAvant la boucle infinie, nous utilisonsvar reaper = LeeksReaper()J’ai construit un objet de récolte de poireaux, puismain()Appel de boucle dans la fonctionreaper.poll()C’est la fonction qui est appelée.
self.pollLa fonction commence à s’exécuter et effectue un travail de préparation avant chaque boucle.self.numTick++Augmenter le nombre,self.updateTrades()Mettez à jour les derniers enregistrements de transactions du marché et calculez les données pertinentes.self.updateOrderBook()Mettre à jour les données du carnet de commandes et calculer les données associées.self.balanceAccount()Vérifiez le solde de l’argent (position).
var burstPrice = self.prices[self.prices.length-1] * BurstThresholdPct # 计算爆发价格
var bull = false # 声明牛市标记的变量,初始为假
var bear = false # 声明熊市标记的变量,初始为假
var tradeAmount = 0 # 声明交易数量变量,初始为0
L’étape suivante consiste à déterminer si le marché à court terme actuel est haussier ou baissier.
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
}
Rappelez-vous, dans l’article précédentself.updateOrderBook()fonction, dans laquelle nous utilisons l’algorithme de moyenne pondérée pour construire une série temporelle avec l’ordrepricesTableau. Trois nouvelles fonctions sont utilisées dans ce code_.min,_.max,sliceCes trois fonctions sont également très faciles à comprendre.
_.min:Sa fonction est de trouver la plus petite valeur dans le tableau de paramètres.
_.max:Sa fonction est de trouver la plus grande valeur dans le tableau de paramètres.
slice: Cette fonction est une fonction membre de l’objet tableau JavaScript. Sa fonction est d’intercepter une partie du tableau en fonction de l’index et de la renvoyer. 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)) // 会截取 4 ~ 1 这几个元素,返回一个新数组:[4,3,2,1]
}

Les conditions pour juger les ours et les taureaux ici sont les suivantes :
self.numTick > 2Pour être établi, cela signifie que lorsqu’un nouveau cycle de prix de détection éclate, il doit être déclenché après au moins trois cycles de détection pour éviter d’être déclenché au début.self.pricesLes dernières données de laself.pricesLa différence entre le prix maximum ou minimum de la plage précédente du tableau doit être rompueburstPriceC’est le prix explosif.Si toutes les conditions sont remplies, alors marquezbulloubear,pourtrue, et donnetradeAmountAffectez des variables et planifiez les transactions Stud.
D’après le précédentself.updateTrades()Mis à jour et calculé dans la fonctionself.vol, pour le paramètreBurstThresholdVolDécidez s’il faut réduire l’intensité des transactions (réduire la taille des transactions prévues).
if (self.vol < BurstThresholdVol) {
tradeAmount *= self.vol / BurstThresholdVol // 缩减计划交易量,缩减为之前量的self.vol / BurstThresholdVol 倍
}
if (self.numTick < 5) {
tradeAmount *= 0.8 // 缩减为计划的80%
}
if (self.numTick < 10) { // 缩减为计划的80%
tradeAmount *= 0.8
}
Ensuite, déterminez si le signal de trading et le volume de trading répondent aux exigences :
if ((!bull && !bear) || tradeAmount < MinStock) { # 如果非牛市并且也非熊市,或者计划交易的量tradeAmount小于参数设置的最小交易量MinStock,poll函数直接返回,不做交易操作
return
}
Après le jugement ci-dessus, exécutezvar tradePrice = bull ? self.bidPrice : self.askPriceDéfinissez le prix de la transaction selon qu’il s’agit d’un marché baissier ou haussier, et attribuez la valeur au prix du connaissement correspondant.
Entrez enfin unwhileLa seule condition pour arrêter la boucle esttradeAmount >= MinStockLe volume de transaction prévu est inférieur au volume de transaction minimum.
Dans la boucle, les ordres sont passés en fonction du caractère haussier ou baissier du marché actuel. Et enregistrez l’ID de commande dans la variableorderId. Après chaque tour de commandeSleep(200)Attendez 200 millisecondes. Alors jugez dans la boucleorderIdEst-ce vrai (si la commande échoue, l’ID de la commande ne sera pas renvoyé et la condition if ne sera pas déclenchée), si la condition est vraie. Obtenez l’ID de commande et attribuez-le àself.tradeOrderId。
Déclarer une variable pour stocker les données de commandeorderLa valeur initiale estnull. Ensuite, faites une boucle pour obtenir les données de commande de cet ID et déterminez si la commande est en statut de commande en attente. Si elle est en statut de commande en attente, annulez la commande de cet ID. Si elle n’est pas en statut de commande en attente, sortez de cet état boucle de détection.
var order = null // 声明一个变量用于保存订单数据
while (true) { // 一个while循环
order = exchange.GetOrder(orderId) // 调用GetOrder查询订单ID为 orderId的订单数据
if (order) { // 如果查询到订单数据,查询失败order为null,不会触发当前if条件
if (order.Status == ORDER_STATE_PENDING) { // 判断订单状态是不是正在挂单中
exchange.CancelOrder(orderId) // 如果当前正在挂单,取消该订单
Sleep(200)
} else { // 否则执行break跳出当前while循环
break
}
}
}
Suivez ensuite le processus ci-dessous :
self.tradeOrderId = 0 // 重置self.tradeOrderId
tradeAmount -= order.DealAmount // 更新tradeAmount,减去提单的订单已经成交的数量
tradeAmount *= 0.9 // 减小下单力度
if (order.Status == ORDER_STATE_CANCELED) { // 如果订单已经是取消了
self.updateOrderBook() // 更新订单薄等数据
while (bull && self.bidPrice - tradePrice > 0.1) { // 牛市时,更新后的提单价格超过当前交易价格0.1就减小交易力度,略微调整交易价格
tradeAmount *= 0.99
tradePrice += 0.1
}
while (bear && self.askPrice - tradePrice < -0.1) { // 熊市时,更新后的提单价格超过当前交易价格0.1就减小交易力度,略微调整交易价格
tradeAmount *= 0.99
tradePrice -= 0.1
}
}
Lorsque le déroulement du programme sautewhile (tradeAmount >= MinStock) {...}Ce cycle indique que le processus de transaction d’explosion des prix est terminé.
mettre en œuvreself.numTick = 0, c’est-à-dire réinitialiserself.numTickest 0.
LeeksReaper()Le constructeur s’exécute enfinselfL’objet renvoyé estvar reaper = LeeksReaper()Lorsqu’il a été retourné àreaper。
Jusqu’à présentLeeksReaper()Nous avons analysé la manière dont le constructeur construit l’objet récolteur de poireaux, les différentes méthodes de l’objet récolteur de poireaux et le processus d’exécution des principales fonctions logiques. Je pense qu’après avoir lu cet article, vous devriez avoir une compréhension plus claire de la haute fréquence processus d’algorithme de stratégie. comprendre.