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

Tutoriel de base sur la rédaction de stratégies avec la plateforme de négociation quantitative FMZ (doit être lu)

Auteur:FMZ~Lydia, Créé: 2023-07-12 15:09:33, mis à jour: 2024-02-05 20:05:43

[TOC] Je vous en prie.

img

Ce tutoriel contient des connaissances de base de l'écriture de stratégie, y compris l'introduction de l'API, backtest, graphiques et plus encore. Après avoir appris ce tutoriel de base, les utilisateurs seront en mesure d'utiliser l'API de base de manière compétente et d'écrire une stratégie de bot stable. Avant d'apprendre le tutoriel, vous devez apprendre à utiliserCommencez avec la plateforme FMZ Quant.

Le tutoriel de l'ancienne version:FMZ Quant (FMZ.COM) Manuel de rédaction de stratégie 2.0 (Tutoriel); il y a beaucoup d'index de postes dans le tutoriel, qui sont recommandés à lire.

Instruction préliminaire sur la rédaction de stratégies

Introduction à l'API

Le programme trading consiste à utiliser des programmes pour se connecter à des plateformes via une API afin d'obtenir des achats et des ventes automatiques ou d'autres fonctions selon l'intention de conception.

À l'heure actuelle, il existe deux protocoles d'interface principaux pour les plateformes de crypto-monnaie: REST et Websocket. Chaque fois que le protocole REST obtient des données, il doit être consulté une fois. Prenons l'API de la plate-forme simulée Wex.app à titre d'exemple. Ouvrez le [lien] (https://api.wex.app/api/v1/public/ticker?market=BTC_USDT) directement dans le navigateur, et vous pouvez obtenir le résultat comme suit:

{"data:{"buy":"11351.73","high":"11595.77","last":"11351.85","low":"11118.45","open":"11358.74","quoteVol":"95995607137.00903936","sell":"11356.02","time":1565593489318,"vol":"3552.5153"}}

De cette façon, vous pouvez voir que le trading suivant les dernières cotations du marché de la paire de trading BTC_USDT, changera à chaque fois qu'il est actualisé; market=" est suivi par les paramètres spécifiques de la paire de trading, qui peuvent être modifiés pour obtenir d'autres données de paire de trading. Pour les interfaces publiques, telles que les cotations du marché, tout le monde peut les obtenir, donc aucune vérification n'est requise. Cependant, certaines interfaces doivent déterminer l'identité de l'utilisateur lors de la passation d'une commande ou de l'obtention d'un compte. Dans ce cas, API-KEY est nécessaire pour signer. Websocket est un mode d'abonnement. Après avoir envoyé le contenu qui doit être souscrit, la plateforme enverra les données mises à jour au programme, et il n'est pas nécessaire de le réviser à chaque fois, donc c'est plus efficace.

La plateforme de trading FMZ Quant encapsule l'interface REST de chaque plateforme, et utilise une façon unifiée d'appeler et un format de données unifié, rendant la rédaction de stratégie plus simple et plus générale.

Différents langages de programmation

La plupart des parties du document de l'API de la plateforme FMZ utilisent JavaScript comme exemple, mais en raison de l'encapsulation, il n'y a presque aucune différence entre les différents langages, et vous n'avez qu'à faire attention aux problèmes de syntaxe. C++" est un peu spécial, et les tutoriels futurs auront une introduction spécialisée. Puisque Js est relativement simple et n'a pas de problèmes de compatibilité, il est recommandé pour les débutants. La plateforme FMZ Quant prend en charge Python complet, et peut librement installer divers paquets. Il est recommandé aux utilisateurs qui ont une certaine base de programmation. Pour les utilisateurs qui ne veulent pas apprendre des langages de programmation et veulent juste écrire des stratégies, la plateforme FMZ Quant prend également en charge rapidement Mylanguage, qui est fondamentalement compatible avec les langages Webstock, et il est recommandé pour ceux qui ont une expérience pertinente. L'inconvénient est que Webstock n'est pas aussi puissant et flexible que les

Comme Python a des versions différentes, il peut être spécifié au début du programme, comme:#!Python2et#!Python3. Notez que JavaScript a récemment mis à jour sa syntaxe ES6, et ceux qui sont intéressés peuvent en apprendre davantage. Les codes Python et Javascript avec les mêmes fonctions sont montrés ci-dessous. On peut voir qu'il n'y a que des différences de syntaxe, donc le document API ne donne que des exemples de Javascript, et ce tutoriel tiendra également compte des cas d'utilisation spéciaux de Python.

#python code
def main():
    while True:
        Log(exchange.GetAccount().Balance)
        Sleep(2000)
#the corresponding Js code
function main(){
    while(true){
        Log(exchange.GetAccount().Balance)
        Sleep(2000)
    }
}

Recommandations pour les ressources

  • Ce tutoriel ne donnera pas d'introduction détaillée sur chaque interface dans la plateforme FMZ API document, de sorte que vous pouvez vérifiercet article pour plus de détails.
  • Si vous souhaitez recevoir le signal tradingview et passer une commande sur FMZ, vous pouvez consultercet article.
  • Pour un démarrage rapide de Javascript et Python, l'écriture de stratégies simples ne nécessite pas de syntaxe complexe, mais seulement quelques concepts de base; vous pouvez étudier le tutoriel pendant que vous apprenez à programmer (https://www.fmz.com/bbs-topic/9123, https://www.fmz.com/bbs-topic/9124).
  • Document en langue mylène; Mylanguage est encore très pratique pour les stratégies de tendance.
  • Voici un exemple d'invocation de C++. Ceux qui s'intéressent à C++ peuvent y jeter un coup d'œil. Comme ce n'est pas un langage interprétatif, le débogage est très difficile, doncl'exemplen'est pas recommandé.
  • Curs de négociation quantitative de crypto-monnaie de NetEase Cloud Classroom, produit officiellement par FMZ, ne nécessite que 20 yuans, avec un contenu riche en détails, du simple au profond, adapté aux débutants!courselink
  • Voilà.certaines stratégies pédagogiquesVous pouvez essayer d'écrire des stratégies pendant que vous étudiez les bases.
  • Explication détaillée du code source de la stratégie:le lien.

Outil de débogage

La plateforme FMZ Quant fournit lesOutil de débogageL'outil de débogage ne prend en charge que JavaScript et ne peut être exécuté que pendant une période de temps; l'interface de la plateforme peut être débogage sans créer de bot; les données de retour seront renvoyées en conséquence et le code de l'outil de débogage ne sera pas enregistré.img

Cadre du programme stratégique

Le programme de stratégie est le même qu'un programme normal, qui est exécuté dans des ordres de code. La partie spéciale est qu'il doit y avoir une fonction main. Puisque la stratégie doit s'exécuter sans interruption, une boucle plus le temps de sommeil est généralement nécessaire. Parce que la fréquence d'accès des API de la plate-forme est limitée, le temps de sommeil doit être ajusté en conséquence. Ce cadre est l'exécution typique à intervalles fixes, et vous pouvez également utiliser Websocket pour écrire des stratégies basées sur des événements. Par exemple, l'exécution immédiate tant que la profondeur change, ce qui sera introduit dans le tutoriel avancé.

D'autres fonctions comportant des actions spéciales sont présentées comme suit:

  • onexit() est une fonction normale à la sortie; sa durée d'exécution maximale est de 5 minutes; elle peut être non spécifiée; si le temps est écoulé, une erreur d'interruption sera signalée.
  • onerror() est une fonction de sortie anormale; sa durée d'exécution maximale est de 5 minutes; elle peut être non spécifiée.
  • init() est une fonction d'initialisation; son programme de stratégie sera appelé automatiquement lorsqu'il commence à s'exécuter; il peut être non spécifié.
function onTick(){
   var ticker = exchange.GetTicker()
   var account = exchange.GetAccount()
    //write the strategy logic here, and it will be called ever 6 seconds
}
function main(){
    while(true){
        onTick()
        Sleep(6000)
    }
}

Dans l'exemple précédent, s'il y a une erreur dans l'accès au réseau, la stratégie peut s'arrêter directement. Si vous voulez une stratégie similaire au redémarrage automatique et qui ne s'arrêtera pas, vous pouvez utiliser la boucle principale tolérante aux erreurs dans la stratégie du bot (n'utilisez pas try pour le backtest). Bien sûr, cela n'est recommandé que lorsque la stratégie est stable, sinon toutes les erreurs ne seront pas signalées, ce qui rend difficile la recherche de défauts dans la stratégie.

function onTick(){
   var ticker = exchange.GetTicker()
   var account = exchange.GetAccount()
    //write the strategy logic here, and it will be called ever 6 seconds
}
function main(){
    try{
        while(true){
           onTick()
           Sleep(6000)
       }
    }catch(err){
        Log(err)
    }
}

Introduction à l'API de la plateforme

Plateforme et paire de négociation

Lorsque vous appelez une API liée à une plateforme, vous devez spécifier la plateforme et la paire de trading.exchangepour représenter cet objet.exchange.GetTicker()La valeur de l'indice de change obtenu est le ticker de marché de cette paire de change.

La plate-forme FMZ Quant prend en charge l'ajout simultané de plusieurs objets paire de négociation d'échange. Par exemple, vous pouvez exploiter BTC et ETH du même compte de plate-forme en même temps, ou vous pouvez exploiter BTC d'un échange et ETH d'un autre échange en même temps. Notez que différents comptes sur la même plate-forme peuvent également être ajoutés en même temps, et ils sont distingués selon les étiquettes ajoutées au site Web FMZ.exchangesles représentent, à savoirexchanges[0]etexchanges[1]...et ainsi de suite, selon l'ordre d'addition lorsque le bot est créé.BTC_USDT, l'ancienne BTC est la monnaie de négociation, et USDT est la monnaie de cotation.

img

Il est évident que si nous opérons beaucoup de paires de trading, cette méthode sera très gênante.exchange.SetCurrency("BTC_USDT"); alors, la paire de négociation liée àexchangedevientBTC_USDT, qui restera valable jusqu'à la prochaine demande de changement de paire de négociation.Notez que le backtest prend en charge la commutation des paires de trading récemmentVous trouverez ci-dessous un exemple concret:

var symbols = ["BTC_USDT", "LTC_USDT", "EOS_USDT", "ETH_USDT"]
var buyValue = 1000
function main(){
  for(var i=0;i<symbols.length;i++){
      exchange.SetCurrency(symbols[i])
      var ticker = exchange.GetTicker()
      var amount = _N(buyValue/ticker.Sell, 3)
      exchange.Buy(ticker.Sell, amount)
      Sleep(1000)
  }
}

Obtenez les interfaces publiques

Comme mentionné dans l'exemple précédent, l'interface de marché est généralement une interface publique, accessible à tous. Les interfaces de marché courantes sont: GetTicker, GetDepth, GetRecords et GetTrades. La cote de marché est la base de la stratégie pour faire des jugements commerciaux. Plus tard, je vais les présenter une par une. Il est préférable de les essayer dans le Debug Tool par vous-même. Si vous avez besoin d'une explication détaillée, vous pouvez le vérifier dans le document API.

Chaque interface comporte généralement unInfole champ, qui représente la chaîne de données d'origine renvoyée par la plateforme, et qui peut être utilisé pour compléter des informations supplémentaires.JSON.parse(), alors que Python utilise la bibliothèque json.TimeLe champ indique l'horodatage de la demande, qui peut être utilisé pour juger du retard.

Lorsque vous utilisez n'importe quelle API dans le bot, l'accès peut échouer et retournernull, et Python retourneNone. À ce moment, les données en cours d'utilisation signalent une erreur et provoquent l'arrêt du bot, donc la tolérance à l'erreur est très importante. Ce tutoriel présentera la tolérance à l'erreur spécialement.

GetTicker

GetTicker est probablement l'interface la plus couramment utilisée. Vous pouvez trouver le prix exécuté la dernière fois, le prix buy1 et le prix sell1, et le dernier volume de trading. Avant de passer un ordre, le prix exécuté peut être déterminé en fonction des informations du ticker.{"Info:{}, "High":5226.69, "Low":5086.37,"Sell":5210.63, "Buy":5208.5, "Last":5208.51, "Volume":1703.1245, "OpenInterest":0, "Time":1554884195976}.

function main() {
    var ticker = exchange.GetTicker()
    Log(ticker) //return ticker in the debugging tool, and you can see the specific result
    Log('Last time executed price:',ticker.Last, 'Buy1 price:', ticker.Buy)
}

Obtenez la profondeur

GetDepth permet d'obtenir des informations approfondies sur les ordres en attente. Bien que GetTicker inclut les prix d'achat 1 et de vente 1, si vous souhaitez interroger des ordres en attente plus profonds, vous pouvez utiliser cette interface, pour généralement vérifier 200 ordres en attente. Les prix de choc peuvent être calculés à l'aide de cette interface. Ci-dessous un résultat de retour réel. Parmi eux, Asks représente l'ordre de vente en attente, et le tableau est Sell1, Sell2... Ainsi, le prix augmente également à son tour. Bids représente l'ordre d'achat en attente, et le tableau est buy1, buy2... Le prix baisse à son tour.

{
    "Info":null,
    "Asks":[
        {"Price":5866.38,"Amount":0.068644},
        {"Price":5866.39,"Amount":0.263985},
        ......
        ]
    "Bids":[
        {"Price":5865.13,"Amount":0.001898},
        {"Price":5865,"Amount":0.085575},
        ......
        ],
    "Time":1530241857399
}

Exemple d'utilisation de GetDepth pour les demandes et les offres:

function main() {
    var depth = exchange.GetDepth()
    Log('Buy 1 price:', depth.Bids[0].Price, 'Sell 1 price:', depth.Asks[0].Price)
}

Obtenez des enregistrements

GetRecords est l'une des interfaces les plus couramment utilisées, peut retourner des informations de prix dans une longue période à la fois, ce qui est la base du calcul de divers indicateurs. Si la période de la ligne K n'est pas spécifiée, cela signifie utiliser la période par défaut lors de l'ajout d'un bot. La longueur de la ligne K ne peut pas être spécifiée et continuera d'augmenter avec le temps. Le nombre maximum est de 2000, et dans le premier appel, le nombre est d'environ 200 (différentes plateformes renvoient des numéros différents). La dernière ligne K est la dernière ligne K, de sorte que les données changeront à mesure que les cotations du marché changent; la première ligne K est la plus ancienne des données.

exchange.SetMaxBarLen(Len)peut définir le nombre de K-lines acquises pour la première fois (prouvé par certaines plateformes) et le nombre maximal de K-lines.Par exemple:exchange.SetMaxBarLen(500).

GetRecords peut spécifier des périodes telles que PERIOD_M1: 1 minute, PERIOD_M5: 5 minutes, PERIOD_M15: 15 minutes, PERIOD_M30: 30 minutes, PERIOD_H1: 1 heure et PERIOD_D1: 1 jour.exchange.GetRecords(PERIOD_M1). Après la mise à niveau du dernier docker, il prendra en charge la personnalisation des périodes, qui passe simplement le deuxième chiffre de la période comme paramètre. La personnalisation au niveau des minutes sera synthétisée selon la ligne K de 1 minute, la ligne K inférieure à 1 minute sera synthétisée via GetTrades ((), et les contrats à terme sur matières premières seront synthétisés selon tick.Notez qu'il y a aussi d'autres variables majuscules commePERIOD_M1dans le tutoriel. Ce sont les variables globales par défaut de FMZ. Si vous êtes intéressé, vous pouvez log leurs valeurs spécifiques par vous-même, et vous pouvez les utiliser directement dans la norme.

Exemple de données renvoyées:

[
    {"Time":1526616000000,"Open":7995,"High":8067.65,"Low":7986.6,"Close":8027.22,"Volume":9444676.27669432},
    {"Time":1526619600000,"Open":8019.03,"High":8049.99,"Low":7982.78,"Close":8027,"Volume":5354251.80804935},
    {"Time":1526623200000,"Open":8027.01,"High":8036.41,"Low":7955.24,"Close":7955.39,"Volume":6659842.42025361},
    ......
]

Exemple de ligne K itérée:

function main(){
    var close = []
    var records = exchange.GetRecords(PERIOD_H1)
    Log('total bars: ', records.length)
    for(var i=0;i<records.length;i++){
        close.push(records[i].Close)
    }
    return close
}

GetTrades

GetTrades obtient les données de trading dans une certaine plage de temps (pas vos propres données de trading), ce qui n'est pas pris en charge par certaines plateformes.

Obtenez un compte pour le commerce

Ces interfaces sont liées au compte, de sorte qu'elles ne peuvent pas être obtenues directement. Pour les obtenir, vous devez utiliser API-KEY pour signer. Après le traitement automatique unifié en arrière-plan de la plate-forme FMZ, vous pouvez les utiliser directement.

Compte de prise de vue

GetAccount pour obtenir les informations du compte. En tant que l'une des interfaces les plus couramment utilisées, il est nécessaire de l'appeler avant de passer une commande, pour éviter un solde insuffisant.{"Stocks":0.38594816,"FrozenStocks":0,"Balance":542.858308,"FrozenBalance":0,"Info":{}}Lorsque Stocks est le solde disponible de la monnaie de négociation de la paire de négociation, FrozenStocks est le solde gelé des ordres non exécutés, Balance est le montant disponible de la monnaie de cotation et FrozenBalance est le solde gelé.BTC_USDT, Stocks fait référence à BTC, et Balance fait référence à USDT.

Notez que le résultat de retour est le résultat de la paire de négociation spécifiée et que les informations sur les autres devises du compte de négociation se trouvent dans le champ Info, vous n'avez donc pas besoin de l'appeler plusieurs fois lorsque vous opérez plusieurs paires de négociation.

Un bot qui imprime constamment la valeur totale de la paire de négociation en cours:

function main(){
    while(true){
        var ticker = exchange.GetTicker()
        var account = exchange.GetAccount()
        var price = ticker.Buy
        var stocks = account.Stocks + account.FrozenStocks
        var balance = account.Balance + account.FrozenBalance
        var value = stocks*price + balance
        Log('Account value is: ', value)
        LogProfit(value)
        Sleep(3000)//sleep 3000ms(3s), A loop must has a sleep, or the rate-limit of the exchange will be exceed
        //when run in debug tool, add a break here
    }
}

Commande

Les méthodes d'invocation incluentexchange.Buy(Price, Amount)etexchange.Buy(Price, Amount, Msg), dans lequel Price indique le prix, Amount est le montant, Msg est une chaîne supplémentaire qui peut être affichée dans le journal du bot, mais n'est pas requise. Ces méthodes sont des ordres en attente. Si l'ordre ne peut pas être complètement exécuté immédiatement, un ordre inachevé sera généré; l'identifiant de commande est retourné si l'ordre est placé avec succès, etnullsera renvoyé si la commande est infructueuse, qui est utilisée pour interroger l'état de la commande.

Si vous voulez passer un ordre d'achat au prix du marché, Price est -1, et Amount est la valeur de l'ordre.exchange.Buy(-1, 0.5); si la paire de négociation estETH_BTC, ce qui signifie que vous achetez 0,5 BTC d'ETH au prix du marché.

Certaines plateformes ont des exigences de précision pour le prix et le montant, qui peuvent être contrôlés avec la fonction de précision_N()Pour les contrats à terme, Buy et Sell ont d'autres significations, qui seront introduites spécialement.

Exemple d'achat une fois atteint le prix correspondant:

function main(){
    while(true){
        var ticker = exchange.GetTicker()
        var price = ticker.Sell
        if(price >= 7000){
            exchange.Buy(_N(price+5,2), 1, 'BTC-USDT')
            break
        }
        Sleep(3000)//Sleep 3000ms
    }
    Log('done')
}

Commande de vente

Les paramètres de l'ordre de marché ont des significations différentes.exchange.Sell(-1, 0.2), c'est-à-dire vendre 0,2 ETH au prix du marché.

Obtenir un ordre

GetOrder obtient les informations de commande basées sur l'identifiant de commande.exchange.GetOrder(OrderId), OrderId est l'identifiant de commande, qui sera renvoyé lors de la passation d'une commande.Typeet la valeur réelle de la commandeStatusLes variables sont des nombres, qui représentent des significations différentes, mais ne sont pas propices à la mémoire.Statusla valeur d'une commande inachevée est 0, ce qui équivaut àORDER_STATE_PENDING. Toutes ces constantes globales peuvent être vues dans le document... Retour du résultat:

{
    "Id":125723661, //Order id
    "Amount":0.01, //Order ammount 
    "Price":7000, //Order price 
    "DealAmount":0, //Executed amount 
    "AvgPrice":0, //executed average price
    "Status":0, //0: not completely executed; 1: executed; 2: canceled 
    "Type":1,//Order type; 0: buy order; 1: sell order 
    "ContractType":"",//contract type, used in futures trading
    "Info":{} //the platform returns the raw information
    }
}

Une stratégie visant à acheter une certaine quantité de monnaie:

function main(){
    while(true){
        var amount = exchange.GetAccount().Stocks
        var ticker = exchange.GetTicker()
        var id = null
        if(5-amount>0.01){
            id = exchange.Buy(ticker.Sell, Math.min(5-amount,0.2))
        }else{
            Log('Job completed')
            return //return the main function, bot will stop
        }
        Sleep(3000) //Sleep 3000ms
        if(id){
            var status = exchange.GetOrder(id).Status
            if(status == 0){ //Here you can aslo use "status == ORDER_STATE_PENDING" to judge 
                exchange.CancelOrder(id)
            }
        }
    }
}

Obtenir des ordres

GetOrder obtient la liste de tous les ordres inachevés de la paire de négociation en cours. S'il n'y a pas d'ordre inachevé, renvoie un tableau vide. Le résultat spécifique de la liste d'ordres, tel que GetOrder.

Exemple d'annulation de tous les ordres de la paire de négociation en cours:

function CancelAll(){
    var orders = exchange.GetOrders()
    for(var i=0;i<orders.length;i++){
        exchange.CancelOrder(orders[i].Id) // cancel order by orderID
    }
}
function main(){
    CancelAll()
    while(true){
        //do something
        Sleep(10000)
    }
}

Annuler la commande

Selon l'identifiant de commande, annuler la commande.exchange.CancelOrder(OrderId). Si l'annulation est réussie, retournez true; sinon, retournez false.

Les contrats à terme et les contrats perpétuels

Pour la crypto-monnaie, le trading à terme est différent du trading au comptant. Les fonctions ci-dessus du trading au comptant s'appliquent également au trading à terme, et le trading à terme unique a ses propres fonctions. Avant de mener le trading de programme de futures de crypto-monnaie, vous devez être familier avec les opérations manuelles sur le site Web et comprendre les concepts de base, tels que l'ouverture, la fermeture, le croisement, l'isolement, le levier, le profit et la perte rapprochés, le revenu flottant, la marge et d'autres concepts, ainsi que les formules de calcul correspondantes. Les tutoriels correspondants peuvent être trouvés sur diverses plateformes à terme, et vous devez les apprendre par vous-même.

Les contrats perpétuels sont similaires aux contrats à terme, mais la différence est qu'il n'existe pas de concept de détention de positions longues et courtes en même temps.

Si la plateforme prend en charge à la fois les contrats à terme et les contrats à découvert, tels que les contrats à découvert d'OKEX et Huobi, vous devez sélectionner OKEX Futures et Huobi Futures séparément sur l'interface de la plateforme à ajouter, car ces plateformes de contrats à découvert sont considérées comme différentes des plateformes à découvert sur FMZ.

Type de contrat défini

La première étape dans le trading de contrats à terme est de définir le contrat à négocier. En prenant les contrats à terme OKEX à titre d'exemple, sélectionnez une paire de trading BTC lors de la création d'un bot ou de backtesting, et vous devez également définir le contrat hebdomadaire, la semaine prochaine ou trimestriel dans le code.invalid contract type. Contrairement aux paires de négociation au comptant, les contrats à terme utilisent souvent une devise de négociation telle que BTC comme marge. L'ajout de BTC à une paire de négociation représente généralement une paire de négociation BTC_USD qui utilise BTC comme marge. S'il existe un contrat à terme avec USDT comme marge, un bot doit être créé pour ajouter la paire de négociation BTC_USDT. Par exemple, les contrats perpétuels comme Binance OKEX Futures, avec des contrats à crypto-marge et à USDT.Après avoir défini la paire de négociation, vous devez également définir le type de contrat spécifique, tel que perpétuel, hebdomadaire, la semaine prochaine, etc. Après avoir défini le contrat, vous pouvez effectuer des opérations telles que l'obtention de devis sur le marché, l'achat et la vente.

Binance, OKEX, HuobiDM, etc. ont à la fois des contrats cryptographiques et des contrats USDT, qui doivent être distingués lors de l'ajout d'un bot et de la définition d'un contrat.

//OKEX Futures
exchange.SetContractType("swap")        // set to perpetual contract
exchange.SetContractType("this_week")   // set to weekly contract 
exchange.SetContractType("next_week")   // set to next week contract 
exchange.SetContractType("quarter")     // set to quarterly contract

//HuobiDM
exchange.SetContractType("this_week")   // set to weekly contract 
exchange.SetContractType("next_week")   // set to next week contract
exchange.SetContractType("quarter")     // set to quarterly contract 
exchange.SetContractType("swap")        // set to perpetual contract

//Binance Futures
exchange.SetContractType("swap")   // set to perpetual contract, and notice that crypto-margined and USDT-margined contracts are all in the perpetual contract
exchange.SetContractType("quarter")   // set to quarterly contract
exchange.SetContractType("next_quarter")  // set to next quarter contract

//BitMEX
exchange.SetContractType("XBTUSD")    // set to perpetual contract
exchange.SetContractType("XBTM19")  // the contract settled at a specific time; for more details, please log in BitMEX to check each contract code

//GateIO
exchange.SetContractType("swap")      // set to perpetual contract, and do not set the default as swap perpetual contract 
//Deribit
exchange.SetContractType("BTC-27APR18")  // the contract settled at a specific time; for more details, please log in Deribit to check out 

Obtenez la position

Pour obtenir la liste actuelle des informations de position, les contrats à terme OKEX (OKCOIN) peuvent passer dans un paramètre pour spécifier le type de contrat à obtenir.[]Il y a beaucoup d'informations spécifiques, qui doivent être analysées en combinaison avec la paire de trading.

Type de données Nom de la variable Définition
objet Les informations la structure brute renvoyée par la plateforme
Numéro Niveau de marge taille de l'effet de levier; OKCoin est 10 ou 20, et la position croisée des contrats à terme OK renvoie 10 (fixé), pour l'API brute ne prend pas en charge
Numéro Montant montant de la position; OKCoin indique la quantité du contrat (entier sur 1)
Numéro Nombre congelé montant de la position gelée
Numéro Le prix prix moyen de la position
Numéro Marge marge congelée
Numéro Résultats les contrats à terme sur matières premières: le profit et la perte de la position marquée sur le marché; crypto-monnaie: unité de crypto-monnaie: BTC/LTC, unité traditionnelle de contrats à terme: RMB (note: dans le cas d'une position croisée de contrats à terme OKCoin, il fait référence au profit et à la perte réalisés, et non au profit et à la perte de la position. Dans la position isolée, il fait référence au profit et à la perte de la position.)
const Le type PD_LONG est la position longue (CTP utilise closebuy_today pour fermer une position); PD_SHORT est la position courte (CTP utilise closesell_today pour fermer une position); dans les contrats à terme CTP, PD_LONG_YD indique la position longue d'hier (qui utilise closebuy pour fermer une position); PD_SHORT_YD est la position courte d'hier (qui utilise closesell pour fermer une position)
une chaîne Type de contrat les contrats à terme sur matières premières sont des codes de contrat, et les actions sont code de plateforme_code de stock; le type de paramètres spécifiques de SetContractType passé
function main(){
    exchange.SetContractType("this_week");
    var position = exchange.GetPosition();
    if(position.length>0){ //especially pay attention to judging the length of position before call, or an error will occur
        Log("Amount:", position[0].Amount, "FrozenAmount:", position[0].FrozenAmount, "Price:",
            position[0].Price, "Profit:", position[0].Profit, "Type:", position[0].Type,"ContractType:", position[0].ContractType)
    }
}

Opérations à terme ouvertes et fermées

Tout d'abord, vous devez définir la taille du levier; méthode d'invocation:exchange.SetMarginLevel(10), où 10 signifie 10 fois l'effet de levier et la taille spécifique de l'effet de levier pris en charge peut être vérifiée dans les plateformes correspondantes,Veuillez noter que l'effet de levier doit être défini dans la plateforme et que le code doit être conforme aux paramètres définis sur la plateforme, sinon une erreur se produira.Vous pouvez aussi le laisser désactivé et utiliser le levier par défaut. Ensuite, définissez la direction de négociation; méthode d'invocation:exchange.SetDirection(Direction), qui correspond aux positions ouvertes et fermées.Contrairement aux contrats à terme, si un contrat perpétuel ne contient pas les concepts de long et de court en même temps, c'est-à-dire qu'une seule position n'est pas autorisée.buyetsellSi elle prend en charge les positions bidirectionnelles, vous devezclosebuy, closesell.Les relations spécifiques:

Opération Paramètres de direction Fonction de mise en place de l'ordre
Position longue ouverte Je suis en train de changer de direction Je suis en train d' acheter.
Fermer une position longue Je suis en train de changer de direction. Je suis en train d' échanger.
Position courte ouverte Je suis en train de changer de direction Je suis en train d' échanger.
Fermer une position courte Je suis en train d'écrire une lettre. Je suis en train d' acheter.

Enfin, il y a le code spécifique pour les positions ouvertes et fermées. Le montant des ordres passés varie d'une plate-forme à l'autre. Par exemple, les contrats à terme Huobi sont basés sur le nombre de contrats, et un contrat est de 100 dollars américains.

function main(){
    exchange.SetContractType("this_week")    // for example, set OKEX futures to weekly contract 
    price = exchange.GetTicker().Last
    exchange.SetMarginLevel(10) // set to 10 times of leverage  
    exchange.SetDirection("buy") // set the order type as buy long 
    exchange.Buy(price+10, 20) // set contract quantity as 20 orders
    pos = exchange.GetPosition()
    Log(pos)
    Log(exchange.GetOrders()) // check out if there is any unfinished order 
    exchange.SetDirection("closebuy"); // if it is a perpetual contract, directly set exchange.SetDirection("sell")
    exchange.Sell(price-10, 20)
}

Donnez un exemple de stratégie spécifique de positions de fermeture complète comme suit:

function main(){
    while(true){
        var pos = exchange.GetPosition()
        var ticker = exchange.GetTicekr()
        if(!ticker){
            Log('not able to obtain ticker')
            return
        }
        if(!pos || pos.length == 0 ){
            Log('no position')
            return
        }
        for(var i=0;i<pos.length;i++){
            if(pos[i].Type == PD_LONG){
                exchange.SetContractType(pos[i].ContractType)
                exchange.SetDirection('closebuy')
                exchange.Sell(ticker.Buy, pos[i].Amount - pos[i].FrozenAmount)
            }
            if(pos[i].Type == PD_SHORT){
                exchange.SetContractType(pos[i].ContractType)
                exchange.SetDirection('closesell')
                exchange.Buy(ticker.Sell, pos[i].Amount - pos[i].FrozenAmount)
            }
        }
        var orders = exchange.Getorders()
        Sleep(500)
        for(var j=0;j<orders.length;j++){
            if(orders[i].Status == ORDER_STATE_PENDING){
                exchange.CancelOrder(orders[i].Id)
            }
        }
    }
}

Échange d'effet de levier sur les crypto-monnaies

Le trading d'effet de levier de crypto-monnaie doit passer au compte d'effet de levier dans le code, et les autres parties sont les mêmes que le trading au comptant.

Utilisationexchange.IO("trade_margin") pour passer au mode compte d'effet de levier; la passation d'un ordre et l'obtention d'actifs de compte accéderont à l'interface de la plateforme d'effet de levier. Utilisationexchange.IO("trade_normal") pour revenir au mode de compte ordinaire.

Plateformes prises en charge:

  • OKEX V3: Les paires de négociation du mode de compte à effet de levier sont différentes de celles ordinaires et certaines paires de négociation peuvent ne pas exister.
  • Huobi: les paires de négociation du mode de compte à effet de levier sont différentes des paires ordinaires et certaines paires de négociation peuvent ne pas exister.
  • ZB: les actifs ne peuvent être transférés qu'en QC. Dans le secteur du trading à effet de levier, les actifs entre les différentes paires de trading sont indépendants, c'est-à-dire que le nombre de pièces QC sous la paire de trading ETH_QC n'est pas visible dans BTC_QC.
  • FCoin
  • Binance

Commerce à terme de matières premières

Les contrats à terme sur matières premières et les contrats à terme sur crypto-monnaie sont très différents. Tout d'abord, le temps de négociation des contrats à terme sur matières premières est très court, mais la crypto-monnaie est négociée pendant 24 heures; le protocole des contrats à terme sur matières premières n'est pas une API REST couramment utilisée; la fréquence de négociation et le montant de la commande en attente des contrats à terme sur matières premières premières sont limités, mais ceux des contrats à terme sur crypto-monnaie sont très lâches, etc. Par conséquent, de nombreux points nécessitent une attention particulière lors de la négociation des contrats à terme sur matières premières premières, et il est recommandé à ceux qui ont une riche expérience des opérations manuelles.https://www.fmz.com/bbs-topic/325Pour ajouter les sociétés à terme sur matières premières:https://www.fmz.com/bbs-topic/371

Les contrats à terme sur matières premières ont mis en place une supervision transparente en juin 2019; pour les programmes individuels, les utilisateurs individuels doivent ouvrir un compte pour demander un code d'autorisation pour les courtiers à terme (le modèle d'information d'application spécifique peut être envoyé au groupe WeChat ou au groupe QQ), ce qui prend généralement 4 à 5 jours; les procédures sont assez compliquées.https://www.fmz.com/bbs-topic/3860. Si votre courtier à terme n'est plus sur la liste, vous pouvez uniquement postuler vous-même, ou ouvrir un nouveau compte d'un courtier pris en charge, ce qui prend généralement 2 jours. FMZ a des relations de coopération approfondies avec certains fournisseurs de services; par exemple, Guotai Junan Hongyuan Futures a acheté la version institutionnelle de la plate-forme FMZ, qui peut être utilisée par ses utilisateurs, et les utilisateurs deviennent automatiquement un VIP lors de l'ouverture d'un compte, et les frais de service sont minimisés.https://www.fmz.com/bbs-topic/506.

En raison des avantages de la structure de la plate-forme FMZ Quant, les utilisateurs peuvent également ajouter plusieurs comptes de courtier à terme et mettre en œuvre certaines fonctions que les autres logiciels de trading de futures de produits de base ne peuvent pas compléter, telles que la synthèse de ticks à haute fréquence; vous pouvez vous référer à:https://www.fmz.com/bbs-topic/1184

Cadre stratégique

Tout d'abord, comme il ne s'agit pas d'un trading 24h et qu'il nécessite une opération de connexion, il est nécessaire de juger de l'état du lien avant de négocier.exchange.IO("status")esttrue, ce qui indique une connexion réussie à la plateforme. Si l'API est appelée lorsque la connexion n'est pas réussie, not login n'est pas demandé. Vous pouvez ajouter Sleep (2000) après le démarrage de la stratégie, lui donnant un certain temps pour se connecter. Vous pouvez également réessayer de vous abonner_C(exchange.SetContractType,"MA888"), ce qui assurera une connexion réussie.

Les codes d'acquisition et de négociation des cotations de marché des contrats à terme sur matières premières sont les mêmes que ceux des contrats à terme sur crypto-monnaie.

function main(){
    _C(exchange.SetContractType,"MA888") //If you do not log in successfully, you cannot subscribe to the contract, so better to try again
    while(true){
        if(exchange.IO("status")){
            var ticker = exchange.GetTicker()
            Log("MA888 ticker:", ticker)
            LogStatus(_D(), "Already connected with CTP !")//_D obtain event
        } else {
            LogStatus(_D(), "Not connected with CTP !")
            Sleep(1000)
        }
    }
}

Il est recommandé d'utiliser la bibliothèque de contrats à terme sur matières premières pour le trading (qui sera décrite plus loin), le code sera très simple à ce stade, et il n'est pas nécessaire de traiter avec des détails fastidieux.https://www.fmz.com/strategy/57029

function main() {
    // Use the CTA strategy framework of commodity futures library 
    $.CTA(Symbols, function(st) {
        var r = st.records
        var mp = st.position.amount
        var symbol = st.symbol
        /*
        "r" represents K-line, "mp" indicates the position amount of the current variety; positive number means long position, negative number means short position, and 0 means no position; "symbol" is the variety name
        if the return value is n: 
            n = 0 : full close positions (no matter now they are long or short)
            n > 0 : if right now long positions are held, add n long positions; if now they are short positions, close n short posiitons; if n is over the position amount right now, reverse to open long positions 
            n < 0 : if right now short positions are held, add n short positions; if now they are long positions, close n long posiitons; if -n is over the position amount right now, reverse to open short positions 
        */
        if (r.length < SlowPeriod) {
            return
        }
        var cross = _Cross(TA.EMA(r, FastPeriod), TA.EMA(r, SlowPeriod));
        if (mp <= 0 && cross > ConfirmPeriod) {
            Log(symbol, "Golden Cross Period", cross, "the moment position", mp);
            return Lots * (mp < 0 ? 2 : 1)
        } else if (mp >= 0 && cross < -ConfirmPeriod) {
            Log(symbol, "Death Cross Period", cross, "the moment position", mp);
            return -Lots * (mp > 0 ? 2 : 1)
        }
    });
}

Modes d'obtention des données du CTP

Les contrats à terme sur matières premières utilisent le protocole CTP, et toutes les cotations du marché et les exécutions d'ordres ne seront notifiées qu'après les changements, tandis que les requêtes concernant les ordres, les comptes et les positions sont des requêtes actives.GetTicker, GetDepthetGetRecords, tous doivent avoir des données mises en cache pour obtenir les dernières données. S'il n'y a pas de données, il attend jusqu'à ce qu'il y ait des données, il n'est donc pas nécessaire que la stratégie utilise Sleep. Lorsqu'il y a des changements de cotation du marché, le ticker, la profondeur et les enregistrements seront mis à jour. À ce moment-là, l'appel de l'une de ces interfaces reviendra immédiatement, et l'état de l'interface appelée sera réglé sur attendre la mise à jour mode. La prochaine fois que la même interface est appelée, elle attendra jusqu'à ce qu'il y ait de nouvelles données retournées. Certains contrats impopulaires ou la limite de prix entraîneront une absence de négociation pendant une longue période, ce qui signifie que la stratégie est bloquée pendant une longue période, également normale.

Si vous voulez obtenir des données à chaque fois que vous obtenez les cotations du marché, même si ce sont des données anciennes, vous pouvez passer au mode de mise à jour immédiate des cotations du marchéexchange.IO("mode", 0). À ce stade, la stratégie ne peut pas être écrite comme étant basée sur des événements, et un événement SLeep doit être ajouté pour éviter une boucle infinie rapide.exchange.IO("mode", 1)pour revenir au mode cache par défaut.

Lors de l'exploitation d'un seul contrat, utilisez le mode par défaut. Cependant, s'il y a plusieurs contrats, il est possible que l'un des contrats ne met pas à jour les devis du marché, ce qui entraîne un blocage de l'interface pour obtenir les devis du marché, et les mises à jour des devis des autres contrats ne peuvent pas non plus être obtenues. Pour résoudre ce problème, le mode de mise à jour immédiate peut être utilisé, mais il est gênant d'écrire des stratégies à haute fréquence.exchange.IO("wait")Si plusieurs objets d'échange sont ajoutés, ce qui est rare dans les contrats à terme sur matières premières, vous pouvez utiliserexchange.IO("wait_any"), et l'indice Index renvoyé indique l'indice de plateforme renvoyé.

Poussée des changements de tick du marché:{Event:"tick", Index: platform index (in the order of the platforms added in the bot), Nano: event of nanosecond-level time, Symbol: contract name}Commande de poussée:{Event:"order", Index:Exchange index, Nano:Event of nanosecond-level time, Order:Order information (same as GetOrder)}

À ce stade, la structure de la stratégie peut être écrite comme suit:

function on_tick(symbol){
    Log("symbol update")
    exchange.SetContractType(symbol)
    Log(exchange.GetTicker())
}

function on_order(order){
    Log("order update", order)
}

function main(){
    while(true){
        if(exchange.IO("status")){ //Judge the link status 
            exchange.IO("mode", 0)
            _C(exchange.SetContractType, "MA888")//Subscribe to MA; only the subscription request for the first time is ture, and the later ones are program switches, which do not consume time
            _C(exchange.SetContractType, "rb888")//Subscribe to rb
            while(true){
                var e = exchange.IO("wait")
                if(e){
                    if(e.event == "tick"){
                        on_tick(e.Symbol)
                    }else if(e.event == "order"){
                        on_order(e.Order)
                    }
                }
           }
        }else{
            Sleep(10*1000)
        }
    }
}

Différences entre les contrats à terme sur matières premières et les crypto-monnaies

Notez également la différence entre les futures de matières premières et les plateformes de crypto-monnaie. Par exemple, GetDepth n'a en fait qu'un seul niveau de profondeur (5 niveaux de profondeur sont chers) et GetTrades ne peut pas obtenir l'historique des transactions (toutes sont simulées sur la base de changements de position et il n'y a pas de véritable enregistrement de négociation). Les futures de matières premières ont un mécanisme de limite de prix. Lorsque la limite est atteinte, le prix d'un ordre de vente de profondeur pour en vendre un est le prix limite, et le volume de l'ordre est de 0. Lorsque la limite est abaissée, le prix d'un ordre d'achat pour en acheter un est le prix limite, et le volume de l'ordre est de 0. En outre, la fréquence de l'interface de requête de futures de matières premières, telles que l'obtention de comptes, d'ordres et de positions, est strictement limitée, généralement toutes les 2 secondes.

Contrats définis

exchange.IO("instruments"): il renvoie la liste de tous les contrats de la plateforme {nom du contrat: détails} sous forme de dictionnaire, et ne prend en charge que les robots.exchange.IO("produits"): il renvoie la liste de tous les éléments de la plateforme {nom du contrat: détails} sous forme de dictionnaire, et ne prend en charge que les robots.exchange.IO("abonné"): il renvoie les cotations de marché souscrites sur la plateforme sous forme de dictionnaire, et ne prend en charge que les robots.

LeContractTypeLes contrats à terme traditionnels des CTP se réfèrent à l'identifiant du contrat, qui est sensible à la casse et à la majuscule.exchange.SetContractType("au1506"). Une fois le contrat défini avec succès, il renverra les informations détaillées du contrat, telles que le montant minimum d'achat, les frais de service, le délai de livraison, etc. Lors de l'abonnement à plusieurs contrats, seule la première fois qu'une demande d'abonnement est effectivement envoyée, et puis la paire de négociation est simplement basculée au niveau du code, ce qui ne prend pas de temps. Le principal contrat continu est le code 888, tel que MA888, le contrat de taux continu est 000, tel que MA000; 888 et 000 sont des contrats virtuels qui ne prennent en charge que le backtest, et les vrais robots ne prennent en charge que les cotations du marché.Cependant, Mylanguage peut exploiter le contrat principal, et le programme changera automatiquement de positions, c'est-à-dire qu'il fermera les positions non principales et ouvrira de nouvelles positions sur les positions principales.

Une connexion non réussie ne permet pas de définir des contrats, mais reviendra immédiatement, vous pouvez donc réessayer par _c, pour savoir que la connexion CTP est terminée.

Position ouverte et position fermée

La direction deSetDirectionpeut obtenir quatre paramètres:buy, closebuy, sell, closesellLes contrats à terme sur matières premières ont plusclosebuy_todayetclosesell_today, indiquant la clôture des positions courantes; le défaut estclosebuy/ closesell, indiquant la clôture des positions d'hier; seules les variétés de la Bourse de Shanghai sont divisées en clôture d'aujourd'hui et clôture d'hier, ce qui peut affecter les frais de service, il est donc nécessaire de donner la priorité à la clôture des positions d'hier. Pour les contrats à terme traditionnels CTP, vous pouvez définir le deuxième paramètre comme 1 ou 2 ou 3, ce qui se réfère respectivement à spéculation, arbitrage et hedge.Les opérations spécifiques telles que l'achat et la vente, l'obtention de positions, l'obtention d'ordres, l'annulation d'ordres et l'obtention de comptes sont les mêmes que le trading à terme de crypto-monnaie, veuillez donc consulter la section précédente.

Opération Paramètres de direction Fonction de mise en place de l'ordre
Position longue ouverte Je suis en train de changer de direction Je suis en train d' acheter.
Fermer une position longue Je suis en train de changer de direction. Je suis en train d' échanger.
Position courte ouverte Je suis en train de changer de direction Je suis en train d' échanger.
Fermer une position courte Je suis en train d'écrire une lettre. Je suis en train d' acheter.

L'exemple suivant est une fonction de position de clôture spécifique. Notez que cet exemple est trop simple. Vous devriez également considérer si elle est dans le temps de négociation, comment réessayer l'ordre en attente si elle n'est pas complètement remplie, quel est le volume maximal de l'ordre, si la fréquence est trop élevée, et si elle est coulissante prix ou prix du marché et ainsi de suite.Il s'agit d'une bibliothèque de plates-formes suggérées pour l'ouverture et la fermeture de positions dans de vrais robots:https://www.fmz.com/strategy/12961Il y a une introduction spécifique dans la section bibliothèque, et il est également recommandé d'étudier les codes sources de la bibliothèque.

function Cover(contractType, amount, slide) {
    for (var i = 0; i < positions.length; i++) {
        if (positions[i].ContractType != contractType) {
            continue;
        }
        var depth = _C(e.GetDepth);
        if (positions[i].Type == PD_LONG || positions[i].Type == PD_LONG_YD) {
            exchange.SetDirection(positions[i].Type == PD_LONG ? "closebuy_today" : "closebuy");
            exchange.Sell(depth.Bids[0]-slide, amount, contractType, positions[i].Type == PD_LONG ? "Close today" : "Close yesterday", 'Bid', depth.Bids[0]);
        } else {
            exchange.SetDirection(positions[i].Type == PD_SHORT ? "closesell_today" : "closesell");
            exchange.Buy(depth.Asks[0]+slide, amount, contractType, positions[i].Type == PD_SHORT ? "Close today" : "Close yesterday", 'Ask', depth.Asks[0]);
        }
    }
}

Les contrats à terme sur matières premières prennent en charge des types d'ordres personnalisés (support pour les robots, mais pas pour les backtests), qui sont spécifiés par un suffixe, joint à _, tels que:

exchange.SetDirection("buy_ioc");
exchange.SetDirection("sell_gtd-20170111")

Suffixes spécifiques:

  • ou annuler THOST_FTDC_TC_IOC
  • gfs valide dans le nœud THOST_FTDC_TC_GFS
  • gfd valable dans la journée THOST_FTDC_TC_GFD
  • gtd valable avant la date spécifiée THOST_FTDC_TC_GTD
  • gtc valable avant l'annulation de THOST_FTDC_TC_GTC
  • gfa valable lors des enchères THOST_FTDC_TC_GFA

Interface de l'Esunny

Par défaut, les interfaces ouvertes dans les courtiers à terme sur matières premières sont toutes des interfaces CTP. Si nécessaire, elles peuvent être remplacées par des interfaces Esunny. Grâce à l'encapsulation de FMZ, la méthode d'invocation est la même.

Les types de commandes personnalisées sont les suivants:

  • gfd valable dans la journée TAPI_ORDER_TIMEINFORCE_GFD
  • gtc valable avant l'annulation de TAPI_ORDER_TIMEINFORCE_GTC
  • gtd valable avant la date spécifiée TAPI_ORDER_TIMEINFORCE_GTD
  • fak partiellement exécuté, annuler le reste TAPI_ORDER_TIMEINFORCE_FAK
  • ioc exécuter immédiatement, ou annuler TAPI_ORDER_TIMEINFORCE_FAK
  • fok n'est pas exécuté complètement, annulez tout TAPI_ORDER_TIMEINFORCE_FOK

Fonctions globales couramment utilisées

Log - Log & WeChat Poussez

Lorsque vous enregistrez un enregistrement de journal sur l'interface du bot, et ajoutez le caractère @ après la chaîne, le message entrera dans la file d'attente de push, et il sera poussé directement après la liaison à WeChat ou telegram, commeLog('Push to WeChat@').

La couleur du journal peut également être personnalisée, commeLog('this is a log in red font #ff0000'). #ff0000est l'hexadecimal de la couleur RVB, indiquant que tous les fichiers journaux sont stockés dans la base de données SqLit du bot dans le répertoire où se trouve le docker, qui peut être téléchargé et ouvert avec un logiciel de base de données, ou peut être utilisé pour copier une sauvegarde et restaurer (le nom de la base de données et l'identifiant du bot sont les mêmes).

LogProfit - Profits d'impression

Il enregistre les profits et dessine la courbe de profit sur l'interface du bot, qui peut être conservée après le redémarrage du bot.LogProfit(1000). Notez que le paramètre deLogProfitn'est pas nécessairement le profit, et il peut être n'importe quel nombre et doit être rempli par vous-même.

LogStatus - Affichage de l'état (y compris les tableaux)

Si l'état du bot, puisque le journal sera sauvegardé en premier et actualisé en permanence, a besoin de l'information uniquement pour l'affichage et non pour l'enregistrement, vous pouvez utiliser leLogStatusLes paramètres deLogStatussont des chaînes, qui peuvent également être utilisées pour représenter les informations du tableau.

Un exemple de tableau d'affichage de la position réelle du bot:

var table = {type: 'table', title: 'position information', cols: ['Column1', 'Column2'], rows: [ ['abc', 'def'], ['ABC', 'support color #ff0000']]}; 
LogStatus('`' + JSON.stringify(table) + '`'); // After serialization, JSON will be added the character "'" on both sides, which is regarded as a comlpex messag format (now supporting tables)
LogStatus('The first line information\n`' + JSON.stringify(table) + '`\nthe third line information'); // the table information can be displayed in multiple lines
LogStatus('`' + JSON.stringify([table, table]) + '`'); // Multiple tables are supported to be displayed at the same time, which will be displayed in one group by TAB  
LogStatus('`' + JSON.stringify(tab1) + '`\n' + '`' + JSON.stringify(tab2) + '`\n

Plus de