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'attributaccount
La 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 variableaccount
Alors, 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 = account
est d'attribuer la variable localeaccount
à l'attributaccount
de 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 * 1000
en millisecondes (CalcNetInterval
Les 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 self.preCalc = now
pour mettre à jour la variable timestampself.preCalc
du dernier bénéfice imprimé à l'horodatage en coursnow
Ici, 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.preNet
est vrai, mettre à jour l'attributself.preNet
qui enregistre la valeurnet
Ensuite, 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 leLogProfit
fonction 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.btc
etself.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.48
Si 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 lewhile
boucle infinie, nous utilisonsvar reaper = LeeksReaper()
pour construire l'objet de la récolte de profit, puisReaper.poll()
est appelé cycliquement dans lemain()
function.
Leself.poll
la 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 temporelleprices
Cette 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 > 2
doit ê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.self.prices
, c'est-à-dire la différence entre les données les plus récentes et le prix maximal ou minimum dans leself.prices
l'ensemble dans la plage précédente devrait briser leburstPrice
.Si toutes les conditions sont vraies, marquerbull
oubear
commetrue
, et attribuer une valeur à la variabletradeAmount
, et planifier un échange de cheval.
Ensuite, pour le paramètreBurstThresholdVol
, sur la base de laself.vol
Les 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.askPrice
Selon 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 unewhile
la 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.orderId
Je vais l' exécuter.Sleep(200)
La boucle juge ensuite si une commande a été passée à la suite d'une commande.orderId
est vrai (si la commande échoue, l'ID de commande ne sera pas renvoyé, et la condition self.tradeOrderId
.
Déclarer une variableorder
pour 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 leself
l'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.