Tutoriel intermédiaire FMZ

Auteur:Le foin, Créé: 2019-04-12 14:28:19, Mis à jour: 2024-02-05 20:07:56

[TOC] Je vous en prie.

img

Ce tutoriel couvrira plus de détails sur la plateforme FMZ, plus de compétences pratiques sur l'utilisation de l'API.

Après avoir appris tout le tutoriel, vous pourrez utiliser pleinement FMZ et être en mesure d'écrire des stratégies plus personnalisées, plus efficaces et plus complexes.

1.Ajouter plusieurs échanges et échanger plusieurs symboles

Vous pouvez négocier sur plusieurs bourses et plusieurs symboles dans un seul robot facilement.

  • Ajoutez un ou plusieurs échangeurs au démarrage du robot.
  • L'API peut être appelée commeexchange.GetTicker()lorsque l'on ajoute un échange
  • Lorsque plusieurs échanges sont ajoutés, l'API est appelée commeexchanges[0].GetTicker(), exchanges[1].GetTicker() img
  • Vous pouvez ajouter le même échange avec différents symboles.img
  • Vous pouvez changer le symbole qui est lié avecexchangeen utilisantIOfonction
var symbols = ["BTC_USDT", "LTC_USDT", "EOS_USDT", "ETH_USDT", "BCC_USDT"]
var buyValue = 1000
function main(){
  for(var i=0;i<symbols.length;i++){
      exchange.IO("currency", symbols[i]) // It is always valid until the next change
      var ticker = exchange.GetTicker()
      var amount = _N(buyValue/ticker.Sell, 3)
      exchange.Buy(ticker.Sell, amount)
      Sleep(1000)
  }
}

2.Tradeles contrats à terme et les contrats de swap

Jusqu'à présent, FMZ prend en charge toutes les principales bourses de contrats à terme, telles que OKEX, HuobiDM, BitMEX, GateIO et Deribit, et leurs contrats de swap.

Pour négocier des contrats à terme sur FMZ, vous devez d'abord ajouter un échange de contrats à terme, définir le symbole lors du démarrage du bot et définir le type de contrat dans votre code.

si une bourse prend en charge à la fois le spot et les contrats à terme, ils doivent être ajoutés à la FMZ séparément.img

L'image ci-dessous montre comment régler le symbole des contrats à terme sur BTC lors du démarrage du bot.img

Vous trouverez ci-dessous comment définir un type de contrat pour chaque échange.

  • Je suis d'accord.
    exchange.SetContractType("swap")        
    exchange.SetContractType("this_week")   
    exchange.SetContractType("next_week")  
    exchange.SetContractType("quarter")   
  • HuobiDM
    exchange.SetContractType("this_week")   
    exchange.SetContractType("next_week")  
    exchange.SetContractType("quarter")     
  • BitMEX
    exchange.SetContractType("XBTUSD")  
    exchange.SetContractType("XBTM19") 
  • La porte
    exchange.SetContractType("swap")    
  • Deribit
    exchange.SetContractType("BTC-PERPETUAL")  
    exchange.SetContractType("BTC-27APR18")

3.A propos des tests de retour

Introduction de base

FMZ dispose de deux modes de backtesting:real ticketsimulate tick. Le niveau de tick réel contient toutes les données historiques terminées (une tick par seconde), de sorte que les résultats de backtesting sont plus fiables. Le niveau de simulation utilise les données cliniques de l'historique à l'intervalle utilisé par votre stratégie. Les ticks à l'intérieur d'une kline sont générés par un algorithme qui est le même que MT4, vous pouvez trouver plus de détails àhttps://www.mql5.com/en/articles/75Pendant ce temps, un intervalle plus court peut être choisi comme base-clins pour générer des tiques. Le mode de simulation de tique est beaucoup plus rapide mais moins précis que le mode de tique réel.

Configuration des tests antérieurs

Voici les paramètres par défaut:imgDes points cachés:img

Résultat des tests antérieurs

img

4.Tolérance aux erreurs

Lorsque vous appelez des fonctions qui accèdent à l'API d'échange (commeGetTicker, Buy, CancelOrder, etc...), vous pouvez obtenir une défaillance d'accès due à un problème de serveur d'échange, à des paramètres incorrects, à un problème de transmission réseau, etc. Dans ce cas, la fonction renverranullVous devez donc savoir comment gérer les erreurs.

Quelle est l'erreur?

Le bot renverra un message d'erreur lorsqu'une erreur se produit. Il suffit de rechercher le nom de l'échange + le message d'erreur, vous pouvez trouver quel est le problème. Par exemple, Une erreur{"result":false,"error_code":20049}est renvoyé lors de l'appelexchange.GetAccount()sur OKEX.OKEX 20049Voici le résultat:imgVous pouvez également vérifier le code d'erreur sur l'échange API document, tels queCode d'erreur OKEX pour les contrats à terme

Erreurs de transaction

Vous devriez considérer comment gérer les erreurs lors de l'écriture du code de stratégie. Voici quelques exemples:

 // 1.Deal when the result is null
 var ticker = exchange.GetTicker()
 while(ticker == null){
     Log('GetTicker error')
     Sleep(100)
     ticker = exchange.GetTicker()
 }
 Log(ticker.Last);
 // 2.Refer when the result is not null
 var ticker = exchange.GetTicker()
 if(!ticker){
     Log(ticker.Last)
 }
 // 3._C() fucntion retry
 var ticker = _C(exchange.GetTicker) // can't  apply _C to CancelOrder, Why?
 Log(ticker.Last)
 // 4. try catch
 try{
     var ticker = exchange.GetTicker()
     Log(ticker.Last)
 }
 catch(err){
     Log('GetTicker error: ', err)
     Log(GetLastError()) //literal means, get last error
 } 

5.Se connecter directement à l'échange

FMZ enveloppe toutes les données des différents échanges dans le même format, ce qui facilite l'écriture d'une stratégie multiplateforme. Cependant, vous ne pouvez pas obtenir les données spécifiques d'une certaine API qui fournissent des informations supplémentaires et ne pouvez pas accéder à l'API que FMZ ne prend pas en charge.

Je suis en train d' écrire.

Retournez le contenu d'origine (chaîne) qui a été retourné par la dernière requête REST API, qui peut être utilisée pour analyser les informations brutes par vous-même.

function main(){
    var account = exchange.GetAccount() //the account doesn't contain all data returned by the request
    var raw = JSON.parse(exchange.GetRawJSON())//raw data returned by GetAccount()
    Log(raw)
}

HttpQuery est une requête

Trouvez tous les détails surHttpQuerysurLe code de l'appareil est le même que celui de l'appareil.

HttpQueryrenvoie les données brutes de cette requête qui doivent être analysées en premier.

//FMZ doesn't have a standard function for exchangeInfo that return the trading-information about all symbols. 
var exchangeInfo = JSON.parse(HttpQuery('https://api.binance.com/api/v1/exchangeInfo'))
Log(exchangeInfo) // FMZ doesn't have a standard function for this API
var ticker = JSON.parse(HttpQuery('https://api.binance.com/api/v1/ticker/24hr'))
Log(ticker)

Pour ces API publiques,HttpQueryC'est une fonction très utile.HttpQueryne prend en charge que JavaScript, pour Python, en utilisant leurlib2ourequestbibliothèque pour envoyer des requêtes http directement.

- Je vous en prie

Pour ces API privées, en utilisantHttpQuerysera très compliqué parce que vous avez besoin de traiter avec API-clef, signe, hash, etc.IOest une fonction pratique pour cette condition, vérifiez-le surLe code de l'appareil est le même que le code de l'appareil.. IODans cette partie, nous nous concentrons uniquement sur l'accès aux API privées.

L'utilisation de cette fonction nécessite d'abord la compréhension de l'API d'origine de l'échange.

  • Tout d'abord, trouvez la page de documentation de l'API BitMEX:https://www.bitmex.com/api/explorer/
  • L'url pour faire un ordre d'arrêt esthttps://www.bitmex.com/api/v1/orderLa méthode estPOST, Les paramètres comprennent le symbole, côté, ordreQty, stopPx,ordType, qui doit être disposé comme "symbole=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=Stop

Le code JavaScript final:

var id = exchange.IO("api", "POST", "/api/v1/order", "symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=Stop")

6.Utiliser le websocket

Fondamentalement, tous les échanges de devises numériques prennent en charge l'envoi de données de marché via websocket, et certains échanges prennent même en charge la mise à jour des informations de compte.

Pour JavaScript, vous pouvez utiliserDialfonction pour connecnt à websocket, Pour Python, vous pouvez utiliserDialouwebsocket_client libray.

Ce tutoriel se concentrera sur la connexion de websockets en utilisant le JavaScript etDialLa fonction Dial a été mise à jour à plusieurs reprises afin d'étendre les différentes utilisations. Ce tutoriel montrera la stratégie basée sur des événements basée sur le websocket et comment se connecter à plusieurs échanges.

Connexion au websocket

  • 1.Dans la plupart des cas, vous pouvez vous connecter directement.Voici ci-dessous un exemple de connexion à Binance all ticker.
    var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
    
  • 2.Pour les données de retour du format comprimé, il faut les spécifier au moment de la connexion.compresssignifie que les données sont compressées et que le paramètremodereprésente si l'envoi ou la réception est compressée.
    var client = Dial("wss://real.okex.com:10441/websocket?compress=true|compress=gzip_raw&mode=recv")
    
  • La fonction Dial prend en charge la reconnexion automatique, qui est effectuée par le langage Go sous-jacent. Pour le contenu des données de la demande est déjà dans l'url, comme l'exemple de Biannce, il est pratique et recommandé. Pour ceux qui ont besoin d'envoyer un message d'abonnement, vous pouvez maintenir la reconnexion par vous-même.
    var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr?reconnect=true")
    
  • 4.Certains échanges canaux d'abonnement sont inclus dans l'URL, comme Binance, mais certains exigent que les utilisateurs envoient des canaux abonnés, comme coinbase:
    var client = Dial("wss://ws-feed.pro.coinbase.com", 60)
    client.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')
    

Recevoir les données

Généralement, les données de websocket peuvent être lues en continu sans sommeil dans une boucle infinie.

function main() {
    var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
    while (true) {
        var msg = client.read() //receve data from client
        var data = JSON.parse(msg) //change raw string to js object
        // do something, don't need sleep. 
    }
}

Le sous-jacent du docker cache toutes les données dans la file d'attente, puis renvoie la première lorsque le programme appellereadLes opérations de réseau du robot, telles que:Buy,GetAccount,CancelOrderPour les informations telles que la poussée de transaction, la poussée de compte, la poussée profonde de sous-ensemble, etc., nous avons besoin de données historiques. Pour les données de marché, nous nous intéressons généralement uniquement aux dernières.

Leread()fonction renvoie les données les plus anciennes dans la file d'attente s'il n'y a pas d'arguments, et bloque quand il n'y a pas de données (le programme est mis en pause ici).read(-2)de retourner immédiatement les dernières données, et de retournernulls'il n'y a pas de données dans la file d'attente (le programme ne sera pas mis en pause).

Connectez-vous à plusieurs websockets

Dans ce cas, il est évident que le programme ne peut pas utiliser simpleread()Les échanges sont généralement effectués de la manière suivante:

function main() {
    var binance = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
    var coinbase = Dial("wss://ws-feed.pro.coinbase.com")
    coinbase.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')
    while (true) {
        var msgBinance = binance.read(-1)
        var msgCoinbase = coinbase.read(-1)
        if(msgBinance){
            // Binance has new data
        }
        if(msgCoinbase){
            // coinbase has new data
        }
        Sleep(1) // just sleep 1ms
    }
}

Cadre général d'utilisation du websocket

Étant donné que les données push ont déjà été utilisées, le programme est naturellement écrit comme un type axé sur les événements, en accordant une attention particulière à la fréquence des demandes d'API.

var tradeTime = Date.now()
var accountTime = Date.now()
function trade(data){
    if(Date.now() - tradeTime > 2000){//only trade once within 2s
        tradeTime = Date.now()
        //trading code
    }
}
function GetAccount(){
    if(Date.now() - accountTime > 5000){//only get account once within 5s
        accountTime = Date.now()
        return exchange.GetAccount()
    }
}
function main() {
    var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr|reconnect=true")
    while (true) {
        var msg = client.read()
        var data = JSON.parse(msg)
        var account = GetAccount()
        trade(data)
    }
}

Tous les pramétrages

Les paramètres deDial(Address, Timeout): Timeout: temps d'arrêt de la connexion L'adresse peut être suivie par d'autres paramètres qui sont liés à&L'adresse et les paramètres sont séparés par|,

Paramètre Définition
compresser Méthode de compression, peut êtregzip_raw, gzip. Utilisations par OKEXgzip_raw
le mode peut êtredualsignifie que l'envoi et la réception doivent être compressés,sendsignifie envoyer besoin d'être comprimé etrecvsignifie recevoir.
représentant les paramètres du proxy pour ss5.socks5://name:pwd@192.168.0.1:1080
reconnecter Reconnect=truepour permettre la reconnexion
l'intervalle intervalest l'intervalle de réessayage, par défaut 1000 ms
charge utile Le message d'abonnement qui doit être envoyé lorsque wss se reconnecte

Les paramètres deread()Le texte est le suivant: Quand la connexion a été coupée,read()renverra une chaîne vide.

Paramètre Aucune -1 -2 2000
la file d'attente n'est pas vide retourner les données les plus anciennes immédiatement retourner les données les plus anciennes immédiatement renvoyer immédiatement les dernières données retourner les données les plus anciennes immédiatement
la file d'attente est vide Bloquer jusqu'à ce que de nouvelles données soient récupérées retournullimmédiatement retournullimmédiatement attendre moins de 2000 ms jusqu'à ce que de nouvelles données soient renvoyées, sinon, retournernull

L'utilisation declose()Le texte est le suivant: Fermez la connexion du websocket.

function main() {
    var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr|reconnect=true")
    client.close()
}
    

7.Asynchrone ou multithreading

Vous avez peut-être remarqué que tous les codes que nous avons maintenant sont à fil unique, l'exécution séquentielle.GOIl s'agit d'un outil très limité.Goquand vous vous souciez vraiment du retard et de la consommation de temps de chaque demande API.

L'échange.Méthode, Args

Méthode : nom de la fonction. Les arguments de la méthode.

Liste des fonctions prises en charge:GetTicker, GetDepth, GetTrades, GetRecords, GetAccount, GetOrders, GetOrder, CancelOrder, Buy, Sell, GetPosition.

Un exemple de JavaScript:

function main(){
    var a = exchange.Go("GetTicker"); //GetTicker Asynchronous multithreaded execution
    var b = exchange.Go("GetDepth");
    var c = exchange.Go("Buy", 1000, 0.1);
    var d = exchange.Go("GetRecords", PERIOD_H1);
    // The above four operations are concurrent multi-threaded asynchronous execution, will not block and immediately return
    var ticker = a.wait(); // Call wait method wait for return to asynchronous get ticker result
    var depth = b.wait(); // Return depth, it is also possible to return null if it fails
    var orderId = c.wait(1000); // Return the order number, 1 second timeout, timeout returns undefined, this object can continue to call wait until the last wait timeout
    var records = d.wait(); // Wait for K-line result
    var ret = d.wait();  // Here waits for an asynchronous operation that has waited and ended, returns null, and logs an error message.
}

wait()la fonction doit être appelée aprèsGofonction, sinon, la ressource de thread s'accumule jusqu'à 2000 et renvoie une erreur.

8.Tables et graphiques

LogStatuset les tableaux

LogStatus enregistrera un message ou des tables sur la barre d'état des bots, se rafraîchira à chaque fois.

//Normal uses of LogStatus
LogStatus(" This is a normal status prompt")
LogStatus(" This is a red font status prompt #ff0000")
LogStatus(" This is a multi-line status message\n I'm the second line")

LogStatus peut enregistrer des tables sur votre page robot.`caractères des deux côtés et le traiter comme un format de message complexe (tableau actuellement pris en charge).

var table = {type: 'table', title: ' Account information support color #ff0000', cols: ['BTC', 'ETH', 'USDT'], rows: [ ['free', 1, 2000], ['frozen', 0, 3000]]}
LogStatus('`' + JSON.stringify(table)+'`')
//Another example, information can also appear in multiple lines:
LogStatus("First line message\n" + JSON.stringify(table)+"`\n third line message")
//Log multiple tables in a group, switching by TAB:
var table1 = {type: 'table', title: ' Account information 1', cols: ['BTC', 'ETH', 'USDT'], rows: [ ['free', 1, 2000], ['frozen', 0, 3000]]}
var table2 = {type: 'table', title: ' Account information 2', cols: ['BTC', 'ETH', 'USDT'], rows: [ ['free', 1, 2000], ['frozen', 0, 3000]]}
LogStatus('`' + JSON.stringify([table1, table2])+'`')

Graphique

Dessinez des chiffres sur la page de gestion des robots. Support graphique HighStocks et HighCharts, voirhttps://www.highcharts.com/demoethttps://www.highcharts.com/stock/demopour plus d'exemples. L'objet Graphique a une__isStockAttribut qui n'existe pas dans le premier.__isStockest false, le graphique s'affiche comme HighCharts.__isStocksi c'est vrai, le graphique s'affichera comme HighStocks.reset()pour effacer les données du graphique.

Un exemple JavaScript de l'utilisation de Graphique pour dessiner les prix de deux symboles:

// This chart is an object in the JS language. Before using the Chart function, we need to declare an object variable chart that configures the chart.
var chart = {
    // Whether the mark is a general chart, if you are interested, you can change it to false and run it.
    __isStock: true,
    tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'},    // Zoom tool
    title : { text : 'Spread Analysis Chart'},          // title
    rangeSelector: {                                    // Selection range
        buttons:  [{type: 'hour',count: 1, text: '1h'}, {type: 'hour',count: 3, text: '3h'}, {type: 'hour', count: 8, text: '8h'}, {type: 'all',text: 'All'}],
        selected: 0,
        inputEnabled: false
    },
    xAxis: { type: 'datetime'},                         // The horizontal axis of the coordinate axis is the x axis and the current setting type is :time
    yAxis : {                                           // The vertical axis of the axis is the y axis, and the default value is adjusted with the data size.
        title: {text: 'Spread'},                        // title
        opposite: false,                                // Whether to enable the right vertical axis
    },
    series : [                                          // Data series, this attribute is saved for each data series (line, K-line graph, label, etc...)
        {name : "line1", id : "Line 1,buy1Price", data : []},  // The index is 0, the data array is stored in the index series of data
        {name : "line2", id : "Line 2,lastPrice", dashStyle : 'shortdash', data : []},
        // The index is 1, dashStyle is set: 'shortdash' ie: Set the dotted line.
    ]
};
function main(){
    var ObjChart = Chart(chart);                      // Call the Chart function to initialize the chart.
    ObjChart.reset();                                 // Empty the chart
    while(true){
        var nowTime = new Date().getTime();           // Get the timestamp of this poll, which is a millisecond timestamp. Used to determine the position of the X axis written to the chart.
        var tickerOne = _C(exchanges[0].GetTicker);   // Get market data
        var tickerTwo = _C(exchanges[1].GetTicker);
        ObjChart.add([0, [nowTime, tickerOne.Last]]); // Use the timestamp as the X value and buy the price as the Y value to pass the index 0 data sequence.
        ObjChart.add([1, [nowTime, tickerTwo.Last]]); // Same as above
        ObjChart.update(chart);                       // Update the chart to show it.
        Sleep(2000);
    }
}

Prend en charge l'affichage de plusieurs figures, un exemple complet:https://www.fmz.com/strategy/136056 img

9.Modèle de stratégie

Template est une bibliothèque qui encapsule de nombreuses fonctionnalités avancées, ce qui facilite l'écriture de votre stratégie.https://www.fmz.com/strategy/27293puis sélectionnez-le sur la page de modification de stratégie.imgLes fonctions sont appelées après$.dans le modèle JavaScript et aprèsext.dans le modèle Python.

function main() {
    var isFirst = true
    while (true) {
        var records = exchange.GetRecords();
        if (records && records.length > 0) {
            $.PlotRecords(records, 'BTC')
            if (isFirst) {
                $.PlotFlag(records[records.length - 1].Time, 'Start', 'S')
                isFirst = false
                $.PlotHLine(records[records.length - 1].Close, 'Close')
            }
        }
        var ticker = exchange.GetTicker()
        if (ticker) {
            $.PlotLine('Last', ticker.Last)
            $.PlotTitle('Last ' + ticker.Last)
        }
        Sleep(60000)
    }
}

Voici un autre exemple simple qui utilise le modèle de plot:https://www.fmz.com/strategy/121917


Plus de

Q25459768Merci.

Le foinJe travaille sur ce tutoriel. Ça prendra quelques jours à terminer. N'hésitez pas à poser n'importe quelle question.