Tutoriel avancé pour la plateforme FMZ Quant

Auteur:Je suis désolée., Créé: 2022-03-22 09:00:57, Mis à jour: 2022-03-29 10:02:52

[TOC] Je vous en prie. Avant d'apprendre ce tutoriel, vous devez étudierCommencez avec la plateforme FMZ QuantetTutoriel élémentaire pour la plateforme FMZ Quant, et devenir compétent dans les langages de programmation.Le tutoriel élémentaire couvre les fonctions les plus couramment utilisées, mais il y a beaucoup de fonctions et de fonctionnalités qui n'ont pas été introduites, et elles ne seront pas couvertes dans ce tutoriel.Après avoir appris ce tutoriel, vous serez en mesure d'écrire plus de stratégies gratuites et personnalisées, et la plate-forme FMZ Quant est juste un outil.

Accès aux données brutes de la plateforme

La plateforme FMZ Quant encapsule toutes les plateformes prises en charge. Afin de maintenir l'uniformité, notre support pour une seule API de plateforme n'est toujours pas complet. Par exemple, GetRecords peut transmettre le nombre de lignes K ou l'heure de début, alors qu'il est fixé sur la plateforme FMZ; certaines plateformes prennent en charge l'ordre par lots, tandis que FMZ ne le prend pas en charge, etc. Il est donc nécessaire de trouver un moyen d'accéder directement aux données de la plateforme.Pour les interfaces publiques (comme les cotations de marché), vous pouvez utiliserHttpQuery, et pour les interfaces cryptées (qui impliquent des informations de compte), vous devez utiliserIO.Pour les paramètres entrants spécifiques, veuillez vous référer au document API de la plateforme correspondant.InfoLe champ renvoie des informations brutes, mais cela ne fait toujours aucune différence sur le problème de ne pas prendre en charge les interfaces.

Je suis en train d' écrire.

Il renvoie le contenu brut (chaînes de caractères) demandé par la dernière API REST, qui peut être utilisé pour analyser l'information étendue par elle-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() Accéder aux interfaces publiques

Pour accéder aux interfaces publiques, Js peut utiliserHttpQuery, et Python peut utiliser des paquets connexes, tels queurllibourequests.

HttpQuery utilise par défaut la méthode GET et prend en charge plus de fonctions; consultez le document API pour plus de détails.

var exchangeInfo = JSON.parse(HttpQuery('https://api.binance.com/api/v1/exchangeInfo'))
Log(exchangeInfo)
var ticker = JSON.parse(HttpQuery('https://api.binance.com/api/v1/ticker/24hr'))
var kline = JSON.parse(HttpQuery("https://www.quantinfo.com/API/m/chart/history?symbol=BTC_USD_BITFINEX&resolution=60&from=1525622626&to=1561607596"))

Exemple de Python utilisant des requêtes:

import requests
resp = requests.get('https://www.quantinfo.com/API/m/chart/history?symbol=BTC_USD_BITFINEX&resolution=60&from=1525622626&to=1561607596')
data = resp.json()

IO accéder aux interfaces cryptées

Pour les interfaces qui nécessitent des signatures API-KEY, la fonction IO peut être utilisée, et les utilisateurs n'ont qu'à se soucier des paramètres entrants, et le processus de signature spécifique sera complété par la sous-couche.

La plateforme FMZ ne prend actuellement pas en charge les ordres stop-loss BitMEX, qui peuvent être mis en œuvre via IO, selon les étapes suivantes:

  • Tout d'abord, trouvez la page d'instructions de l'API BitMEX:https://www.bitmex.com/api/explorer/;
  • Ensuite, trouvez l'adresse de commande de BitMEX à:https://www.bitmex.com/api/v1/order, avec la méthode dePOST; pour FMZ a déjà spécifié en interne l'adresse de base, il vous suffit de passer /api/v1/order.
  • les paramètres correspondants:symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=Stop.

Code spécifique:

var id = exchange.IO("api", "POST", "/api/v1/order", "symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=Stop")
// You can also pass in the object 
var id = exchange.IO("api", "POST", "/api/v1/order", "", JSON.stringify({symbol:"XBTUSD",side:"Buy",orderQty:1,stopPx:4000,ordType:"Stop"}))

Plus d'exemples d'OI:https://www.fmz.com/bbs-topic/3683

Utiliser le websocket

Fondamentalement, toutes les plates-formes de crypto-monnaie prennent en charge le websocket pour envoyer des devises de marché, et certaines plates-formes prennent en charge le websocket pour mettre à jour les informations du compte.

Cet article présentera principalement comment utiliser le langage JavaScript et comment utiliser la fonction Dial encapsulée par la plate-forme pour se connecter, sur la plate-forme FMZ Quant; pour des instructions et paramètres spécifiques sont dans le document, vous pouvez rechercher Dial; pour réaliser diverses fonctions, la fonction Dial a été mise à jour à plusieurs reprises. Cet article couvrira cela et introduit des stratégies basées sur des événements basées sur wss, ainsi que la question de la connexion de plusieurs plates-formes. Python peut également utiliser la fonction Dial, ou la bibliothèque correspondante.

1. Connexion Websocket

Généralement, connectez-vous directement par Websocket; par exemple, pour obtenir Binance tricker push:

var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")

Si les données renvoyées sont en format comprimé, une spécification doit être faite lors de la connexion; compress fait référence au format comprimé et mode représente les données renvoyées qui doivent être compressées; par exemple, lors de la connexion avec OKEX:

var client = Dial("wss://real.okex.com:10441/websocket?compress=true|compress=gzip_raw&mode=recv")

La fonction Dial prend en charge la reconnexion, qui est effectuée par le Golang sous-jacent. Si la connexion détectée tombe en panne, elle sera reconnectée. Pour les données de demande déjà dans l'url, comme l'exemple de Binance tout à l'heure, c'est très pratique et recommandé. Pour ceux qui ont besoin d'envoyer des messages d'abonnement, ils peuvent maintenir le mécanisme de reconnexion eux-mêmes.

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

Pour s'abonner aux messages wss, certaines demandes de plateforme se trouvent dans l'url, et certaines doivent envoyer les canaux abonnés eux-mêmes, comme coinbase:

client = Dial("wss://ws-feed.pro.coinbase.com", 60)
client.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')

Connexion d'interface chiffrée

Généralement, le websocket est utilisé pour lire les devis du marché, mais il peut également être utilisé pour obtenir des ordres et des push de compte. La push de ces données cryptées a parfois un long délai et doit être utilisée avec prudence. Comme la méthode de cryptage est plus compliquée, voici quelques exemples donnés à titre de référence. Notez que seul AccessKey est requis, qui peut être défini comme paramètre de stratégie. Si SecretKey est nécessaire, il peut être appelé implicitement par la fonction exchange.HMAC(() pour assurer la sécurité.

    //Push example of Huobi Futures
    var ACCESSKEYID = 'accesskey of your Huobi account'
    var apiClient = Dial('wss://api.hbdm.com/notification|compress=gzip&mode=recv')
    var date = new Date(); 
    var now_utc =  Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(),date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
    var utc_date = new Date(now_utc)
    var Timestamp = utc_date.toISOString().substring(0,19)
    var quest = 'GET\napi.hbdm.com\n/notification\n'+'AccessKeyId='+ACCESSKEYID+'&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=' + encodeURIComponent(Timestamp)
    var signature = exchange.HMAC("sha256", "base64", quest, "{{secretkey} }") // Remove the extra blank spaces between }}
    auth = {op: "auth",type: "api",AccessKeyId: ACCESSKEYID, SignatureMethod: "HmacSHA256",SignatureVersion: "2", Timestamp: Timestamp, Signature:encodeURI(signature)}
    apiClient.write(JSON.stringify(auth))
    apiClient.write('{"op": "sub","cid": "orders","topic": "orders.btc'}')
    while (true){
        var data = datastream.read()
        if('op' in data && data.op == 'ping'){
            apiClient.write(JSON.stringify({op:'pong', ts:data.ts}))
        }
    }
    
    // Push example of Binance; pay attention that listenKey needs to be updated regularly   
    var APIKEY = 'accesskey of your Binance account'
    var req = HttpQuery('https://api.binance.com/api/v3/userDataStream',{method: 'POST',data: ''},null,'X-MBX-APIKEY:'+APIKEY);
    var listenKey = JSON.parse(req).listenKey;
    HttpQuery('https://api.binance.com/api/v3/userDataStream', {method:'DELETE',data:'listenKey='+listenKey}, null,'X-MBX-APIKEY:'+APIKEY);
    listenKey = JSON.parse(HttpQuery('https://api.binance.com/api/v3/userDataStream','',null,'X-MBX-APIKEY:'+APIKEY)).listenKey;
    var datastream = Dial("wss://stream.binance.com:9443/ws/"+listenKey+'|reconnect=true',60);
    var update_listenKey_time =  Date.now()/1000;
    while (true){
        if (Date.now()/1000 - update_listenKey_time > 1800){
            update_listenKey_time = Date.now()/1000;
            HttpQuery('https://api.binance.com/api/v3/userDataStream', {method:'PUT',data:'listenKey='+listenKey}, null,'X-MBX-APIKEY:'+APIKEY);
        }
        var data = datastream.read()
    }

    // push example of BitMEX
    var APIKEY = "your Bitmex API ID"
    var expires = parseInt(Date.now() / 1000) + 10
    var signature = exchange.HMAC("sha256", "hex", "GET/realtime" + expires, "{{secretkey} }")// secretkey is automatically replaced during execution, so no need to fill in
    var client = Dial("wss://www.bitmex.com/realtime", 60)
    var auth = JSON.stringify({args: [APIKEY, expires, signature], op: "authKeyExpires"})
    var pos = 0
    client.write(auth)
    client.write('{"op": "subscribe", "args": "position"}')
    while (true) {
        bitmexData = client.read()
        if(bitmexData.table == 'position' && pos != parseInt(bitmexData.data[0].currentQty)){
            Log('position change', pos, parseInt(bitmexData.data[0].currentQty), '@')
            pos = parseInt(bitmexData.data[0].currentQty)
        }
    }

3.Lire le Websocket

Généralement, il peut être lu en continu dans une boucle infinie.

function main() {
    var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr");
    while (true) {
        var msg = client.read()
        var data = JSON.parse(msg) // Parse json strings into quotable objects 
// Process data 
    }
}

La vitesse de publication des données wss est très rapide. La sous-couche de Golang cache toutes les données dans la file d'attente, et lorsque le programme appelle à lire, les données seront retournées à leur tour. Cependant, des opérations telles que la passation d'un ordre sur le bot entraîneront des retards, ce qui peut entraîner l'accumulation de données. Pour des informations telles que la publication de l'exécution des transactions, la publication du compte et l'interpolation de la profondeur, nous avons besoin des données d'historique. Pour les données de marché de devis, dans la plupart des cas, nous ne nous soucions que des dernières données, pas des données d'historique.

Siread()Si vous voulez les données les plus récentes, vous pouvez utiliserclient.read(-2)pour renvoyer les dernières données immédiatement, mais lorsqu'il n'y a pas de données, il renvoie nul, ce qui doit être jugé avant référence.

Selon la façon de traiter les anciennes données mises en cache et si elle est bloquée lorsqu'il n'y a pas de données, read a des paramètres différents, comme indiqué dans le tableau ci-dessous, ce qui semble compliqué, mais rend le programme plus flexible.img

4.Connexion avec plusieurs plateformes par Websocket

Dans ce cas, il est évident que le simple fait d'utiliser read() ne fonctionne pas dans le programme, car une plate-forme bloquera les messages en attente, et une autre plate-forme ne recevra pas même s'il y a de nouveaux messages.

    function main() {
        var binance = Dial("wss://stream.binance.com:9443/ws/!ticker@arr");
        var coinbase = Dial("wss://ws-feed.pro.coinbase.com", 60)
        coinbase.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')
        while (true) {
            var msgBinance = binance.read(-1) // Parameter -1 represents no data and return null immediately; it will not occur that being blocked before there is data to be returned 
            var msgCoinbase = coinbase.read(-1)
            if(msgBinance){
                // at this time, Binance has data to return 
            }
            if(msgCoinbase){
                // at this time, coinbase has data to return 
            }
            Sleep(1) // Sleep for 1 millisecond
        }
    }

5.Problèmes de déconnexion et de reconnexion

Cette partie du traitement est plus gênante, car les données de poussée peuvent être interrompues, ou le délai de poussée est extrêmement long. Même si le battement cardiaque peut être reçu, cela ne signifie pas que les données sont toujours poussées. Vous pouvez définir un intervalle d'événement; si aucune mise à jour n'est reçue après l'intervalle, reconnectez-vous; il est préférable de comparer les résultats retournés par rest après une période de temps, pour voir si les données sont exactes. Pour les cas spéciaux de Binance, vous pouvez directement définir la reconnexion automatique.

6.Utiliser le cadre de programme général de Websocket

Pour les données push ont été utilisés, le programme sera naturellement écrit comme événement déclenché; faire attention à la fréquence de données push, parce que les demandes à haute fréquence conduira à être bloqué; en général, vous pouvez écrire:

    var tradeTime = Date.now()
    var accountTime = Date.now()
    function trade(data){
        if(Date.now() - tradeTime > 2000){//Here it limits only one trade in 2 seconds 
            tradeTime = Date.now()
            // Trading logic
        }
    }
    function GetAccount(){
        if(Date.now() - accountTime > 5000){//Here it limits GetAccount only once in 5 seconds 
            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)
        }
    }

7.Conclusion

La méthode de connexion, la méthode de transmission de données, le contenu souscrit et le format de données du websocket sur chaque plate-forme sont souvent différents, de sorte que la plate-forme ne l'encapsule pas et doit utiliser la fonction Dial pour se connecter par elle-même.

PS: Bien que certaines plates-formes ne fournissent pas de citations de websocket, en fait, lorsque vous vous connectez au site Web pour utiliser la fonction de débogage, vous constaterez qu'elles utilisent toutes le websocket push.

Concurrence de plusieurs fils

JavaScript peut réaliser la concurrence par la fonction Go, et Python peut utiliser la bibliothèque multithread correspondante.

Lors de la réalisation de stratégies quantitatives, l'exécution simultanée peut réduire le délai et améliorer l'efficacité. Prenons le bot de stratégie de couverture à titre d'exemple.

var depthA = exchanges[0].GetDepth()
var depthB = exchanges[1].GetDepth()

Lorsqu'une requête d'API de repos est retardée, par exemple le temps de retard est de 100 millisecondes, alors le temps pour obtenir la profondeur deux fois est en fait différent; si plus d'accès sont nécessaires, les problèmes de retard seront plus évidents, ce qui affectera l'exécution de la stratégie.

Comme JavaScript n'a pas de multithread, la sous-couche encapsule la fonction Go pour résoudre ce problème.GetDepth, GetAccountet ainsi de suite.IOest également soutenue, par exemple:exchange.Go("IO", "api", "POST", "/api/v1/contract_batchorder", "orders_data=" + JSON.stringify(orders)), mais en raison du mécanisme de conception, il est plus fastidieux à mettre en œuvre.

var a = exchanges[0].Go("GetDepth")
var b = exchanges[1].Go("GetDepth")
var depthA = a.wait() // Call "wait" method to wait for the return of the asynchronous GetDepth result 
var depthB = b.wait()

Dans la plupart des cas simples, écrire des stratégies de cette façon est parfait. Mais notez que le processus est répété à chaque fois que la stratégie boucle, et les variables intermédiaires a et b sont en fait seulement temporairement auxiliaires. Si nous avons beaucoup de tâches simultanées, nous devons en outre enregistrer la correspondance entre a et la profondeurA, b et la profondeurB. Lorsque nos tâches simultanées sont incertaines, la situation est plus compliquée. Par conséquent, nous espérons réaliser une fonction: lors de l'écriture de la fonction Go simultanément, lier une variable en même temps; lorsque le résultat de l'exécution simultanée retourne, la valeur du résultat est automatiquement attribuée à la variable, éliminant ainsi le besoin de faire des variables intermédiaires et le programme plus concis.

function G(t, ctx, f) {
    return {run:function(){
        f(t.wait(1000), ctx)
    }}
}

Nous avons défini une fonction G, où le paramètre t est la fonction Go à exécuter, ctx est la fonction d'enregistrement du contexte du programme, et f est la fonction qui assigne une valeur spécifique.

À ce stade, le cadre de programmation global peut être écrit sous forme de modèle, similaire au modèle producteur-consommateur (avec quelques différences), le producteur envoie continuellement des tâches et le consommateur les exécute simultanément.

var Info = [{depth:null, account:null}, {depth:null, account:null}] // If we need to obtain the depth and account of the two platforms, more information can also be put in, such as order ID and status, etc.
var tasks = [ ] // Global task list 

function produce(){ // Issue all kinds of concurrent tasks
  // Here the task producing logic has been omitted, only for demo
  tasks.push({exchange:0, ret:'depth', param:['GetDepth']})
  tasks.push({exchange:1, ret:'depth', param:['GetDepth']})
  tasks.push({exchange:0, ret:'sellID', param:['Buy', Info[0].depth.Asks[0].Price, 10]})
  tasks.push({exchange:1, ret:'buyID', param:['Sell', Info[1].depth.Bids[0].Price, 10]})
}
function worker(){
    var jobs = []
    for(var i=0;i<tasks.length;i++){
        var task = tasks[i]
        jobs.push(G(exchanges[task.exchange].Go.apply(this, task.param), task, function(v, task) {
                    Info[task.exchange][task.ret] = v // Here "v" is the return value of the concurrent Go function "wait()", and you can think about it 
                }))
    }
    _.each(jobs, function(t){
            t.run() // Here all tasks are executed concurrently 
        })
    tasks = []
}
function main() {
    while(true){
        produce()         // Give trading command
        worker()        // Concurrently execute
        Sleep(1000)
    }
}

Il semble que seule une fonction simple ait été mise en œuvre dans les opérations ci-dessus. En fait, cela a grandement simplifié la complexité du code. Nous n'avons qu'à nous soucier des tâches que le programme doit générer, et le programme worker() les exécutera automatiquement en parallèle et renverra les résultats correspondants. La flexibilité s'est beaucoup améliorée.

Dessiner selon la fonction graphique

Dans le tutoriel élémentaire, la bibliothèque de classes de dessin est recommandée dans l'introduction du dessin, ce qui peut répondre aux besoins dans la plupart des cas.

Les paramètres internes deChart({…})sont les objets de HighStock et HighCharts, mais un paramètre supplémentaire__isStockFMZ prend en charge les modules de base de HighCharts et HighStock, mais ne prend pas en charge les modules supplémentaires.

L'exemple spécifique de HighCharts:https://www.highcharts.com/demo; exemple HighStock:https://www.highcharts.com/stock/demoVous pouvez vous référer aux codes dans ces exemples, et les transplanter à FMZ commodément.

Vous pouvez appeler add ([index de série ((comme 0), données]) pour ajouter des données dans la série avec l'index spécifié. Appeler reset() pour effacer les données du graphique; reset peut prendre un paramètre de nombre et spécifier le montant à enregistrer. L'affichage de plusieurs graphiques est pris en charge, qui n'a besoin que de passer dans les paramètres du tableau pendant la configuration, tels que: var chart = Chart (([{...}, {...}, {...}]). Par exemple, si Chart1 a deux séries, Chart2 a une série, et Chart3 a une série, lors de l'appel add, les identifiants de série 0 et 1 sont spécifiés pour représenter séparément les données des deux séries dans le graphique mis à jour; la série 2 est spécifiée pour représenter les données de la première série dans Chart2; la série 3 est spécifiée pour représenter le premier des données de la série dans Chart3.

Un exemple spécifique:

var chart = { // This "chart" in JS is an object; before using the Chart function, we need to declare the object variable of a configured chart "chart" 
    __isStock: true,                                    // Mark whether it is a general chart; you can change it to false and try to operate it, if you are interested 
    tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'},    // Zoom tool
    title : { text : 'spread chart'},                       // Theme
    rangeSelector: {                                    // Choose the 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'},                         // Horizontal axis, namely X axis; currently set type: time
    yAxis : {                                           // Vertical axis, namely Y axis; the default changes according to the data 
        title: {text: 'spread'},                           // Theme
        opposite: false,                                // whether to enable the vertical axis on the right 
    },
    series : [                                          // Data series; the attribute saves all kinds of data series (lines, K-lines, labels, etc.)
        {name : "line1", id : "line1,buy1Price", data : []},  // The index is 0; the data stroed in the data array is the data of the index series 
        {name : "line2", id : "line2,lastPrice", dashStyle : 'shortdash', data : []}, // The index is 1; set  dashStyle: 'shortdash', namely: set  dashed line
    ]
};
function main(){
    var ObjChart = Chart(chart);  // Call the Chart function, and initialize the chart 
    ObjChart.reset();             // Empty 
    while(true){
        var nowTime = new Date().getTime();   // Obtain the timestamp of this polling, namely a millisecond tiemstamp, to ensure the location of writing to the X axis in the chart 
        var ticker = _C(exchange.GetTicker);  // Obtain the market quotes data
        var buy1Price = ticker.Buy;           // Get buy one price from the return value of the market quotes 
        var lastPrice = ticker.Last + 1;      // Get the final executed price, and we add 1 to split the 2 lines 
        ObjChart.add([0, [nowTime, buy1Price]]); // Use the timestamp as the value of X, and buy one price as the value of Y; pass in the data series of index 0  
        ObjChart.add([1, [nowTime, lastPrice]]); // Same as above. 
        Sleep(2000);
    }
}

Exemple d'utilisation de la mise en page du graphique:https://www.fmz.com/strategy/136056

Test de retour avancé

Test de rétroaction local Python

Adresse spécifique open source:https://github.com/fmzquant/backtest_python

Installation

Entrez la commande suivante dans la ligne de commande:

pip install https://github.com/fmzquant/backtest_python/archive/master.zip

Un exemple simple

Définissez les paramètres de backtest au début du code de stratégie sous forme de commentaire, et les détails s'afficheront sur le bouton Enregistrer les paramètres sur la page Modifier la stratégie du site Web de la FMZ.

'''backtest
start: 2018-02-19 00:00:00
end: 2018-03-22 12:00:00
period: 15m
exchanges: [{"eid":"OKEX","currency":"LTC_BTC","balance":3,"stocks":0}]
'''
from fmz import *
task = VCtx(__doc__) # initialize backtest engine from __doc__
print exchange.GetAccount()
print exchange.GetTicker()
print task.Join() # print backtest result

Test de retour

Pour les stratégies complètes ont besoin de boucles infinies, l'erreur EOF sera soulevée après le backtest est terminée; par conséquent, nous devrions faire la tolérance aux erreurs bien.

# !/usr/local/bin/python
# -*- coding: UTF-8 -*-

'''backtest
start: 2018-02-19 00:00:00
end: 2018-03-22 12:00:00
period: 15m
exchanges: [{"eid":"Bitfinex","currency":"BTC_USD","balance":10000,"stocks":3}]
'''

from fmz import *
import math
import talib

task = VCtx(__doc__) # initialize backtest engine from __doc__

# ------------------------------ Start of the Strategy  --------------------------

print exchange.GetAccount()     # Call some interfaces, and print their return values 
print exchange.GetTicker()

def adjustFloat(v):             # the custom functions in the strategy 
    v = math.floor(v * 1000)
    return v / 1000

def onTick():
    Log("onTick")
    # Specific strategy code 


def main():
    InitAccount = GetAccount()
    while True:
        onTick()
        Sleep(1000)

# ------------------------------ End of the Strategy --------------------------

try:
    main()                     # The end of the backtest will raise EOFError() to stop stop the backtest loop. Therefore, we should handle with the error, and call task.Join() to print the backtest result, after the error is detected  
except:
    print task.Join()         

Données personnalisées de backtest

exchange.SetData(arr) commute la source de données de backtest et utilise des données de ligne K personnalisées. Le paramètre arr est un tableau dont les éléments sont des données de barres de ligne K (c'est-à-dire: tableau de données de ligne K, qui ne prend temporairement en charge que le backtest JavaScript.

dans le tableau arr, le format de données d'un seul élément:

[
    1530460800,    // time     Timestamp 
    2841.5795,     // open     Open Price 
    2845.6801,     // high     Highest Price
    2756.815,      // low      Lowest Price
    2775.557,      // close    Close Price
    137035034      // volume   Executed Volume 
]

La source de données peut être importée dans le Modèle.

function init() {                                                          // The init function in the template will be executed first when the template is loaded; ensure the exchange.SetData(arr) function is executed first, initialized, and set the data to the backtest system
    var arr = [                                                            // The K-line data to be used during backtest
        [1530460800,2841.5795,2845.6801,2756.815,2775.557,137035034],      // The data of the earliest K-line bar
        ... ,                                                              // If the K-line data is too long, use "..." to represent the omitted data here
        [1542556800,2681.8988,2703.5116,2674.1781,2703.5116,231662827]     // The data of the latest K-line bar 
    ]
    exchange.SetData(arr)                                                  // Import the custom data mentioned above 
    Log("Import data successfully")
}

Remarque: assurez-vous d'importer les données personnalisées en premier (c'est-à-dire appeler la fonction exchange.SetData pour définir les données) lors de l'initialisation. La période de données personnalisée de la ligne K doit être cohérente avec la période de ligne K de la couche inférieure définie sur la page de backtest, c'est-à-dire: données de ligne K personnalisées ; le temps d'une ligne K est de 1 minute, donc la période de la ligne K de la couche inférieure définie dans le backtest doit également être définie à 1 minute.

Utilisation de plateformes non prises en charge par FMZ

Si l'API d'une plate-forme non prise en charge est exactement la même que celle d'une plate-forme prise en charge, sauf l'adresse de base, la plate-forme non prise en charge peut être prise en charge en basculant l'adresse de base. Pour être précis, sélectionnez une plate-forme prise en charge lors de l'ajout d'une plate-forme, mais remplissez la clé API de la plate-forme non prise en charge, et utilisez IO pour basculer l'adresse de base dans la stratégie, comme:

exchange.IO("base", "http://api.huobi.pro") 
//http://api.huobi.pro is the base address of the unsupported platform API, and notice not to add api/v3 and so on, for the address will be automatically completed

Toutes les plateformes ne sont pas prises en charge par FMZ, mais notre plateforme a fourni la méthode d'accès du protocole général.

  • Quand vous écrivez le code pour accéder à une plateforme, le programme crée un service web.
  • Lorsque vous ajoutez une plateforme à FMZ, spécifiez l'adresse et le port du service Web.
  • Lorsque le docker exécute le bot de plateforme du protocole général, la demande d'accès API dans la stratégie sera envoyée au protocole général.
  • Le protocole général accède à la plateforme par demande et renvoie le résultat au dock.

En termes simples, le protocole général est comme un intermédiaire, procédant à la demande du docker et renvoyant les données, selon la norme correspondante. Le code du protocole général doit être complété par vous-même. Écrire le protocole général signifie en fait que vous pouvez accéder uniquement à la plate-forme et compléter la stratégie.

Introduction spécifique du protocole général:https://www.fmz.com/bbs-topic/9120Exemple d'écriture du protocole général en Python:https://www.fmz.com/strategy/101399

Créer votre propre plateforme quantitative

Tout comme diverses opérations d'une plate-forme peuvent être implémentées via l'API, le site Web FMZ est également basé sur l'API. Vous pouvez demander votre propre API-KEY du site Web FMZ pour réaliser des fonctions, telles que create, restart, DeleteRobot, GetRobotList GetRobotLogs, etc. Veuillez vous référer à la section API Extension of FMZ Platform dans le document API pour plus de détails.

En raison de la puissante extensibilité de la plateforme FMZ Quant, vous pouvez créer votre propre plateforme quantitative basée sur l'extension API, permettant à vos utilisateurs d'exécuter des bots sur votre plateforme, etc. Référence spécifique:https://www.fmz.com/bbs-topic/1697 .

Devenir partenaire du FMZ

Promotion de la classe NetEase

Le marché du trading de crypto-monnaie a attiré de plus en plus l'attention des traders quantitatifs en raison de sa particularité. En fait, le trading par programme est devenu le courant dominant de la crypto-monnaie, et des stratégies telles que la couverture et la création de marché sont toujours actives sur le marché. Les débutants ayant une base de programmation faible veulent entrer dans ce nouveau domaine, confrontés à de nombreuses plateformes et à des API changeantes, pleines de difficultés.www.fmz.comPour suivre le cours sur NetEase, il ne vous faut que 20 yuans, et le cours est totalement pour les débutants.

Promotion de l'emploiCours de négociation quantitative de crypto-monnaie sur NetEase Cloud Classroom. Connectez-vous à NetEase Cloud Classroom et partagez votre lien de cours (le lien a un ID de cours unique). D'autres, qui s'inscrivent et achètent le cours via ce lien, vous apporteront 50% du total en commission, à savoir 10 yuans. Suivez le compte public WeChat de NetEase Cloud Classroom Premium Course Promotion pour retirer l'argent. Vous êtes également invités à inviter d'autres personnes à promouvoir le cours sur Weibo ou le groupe QQ.

Affiliation

Les consommateurs qui cliquent sur le lien promotionnel, s'inscrivent et rechargent dans un délai d'un an et demi bénéficieront de la politique selon laquelle notre société remboursera en fonction du montant effectif dans l'ordre valide. La commission sera remboursée au compte du promoteur sous forme de points. Les utilisateurs peuvent échanger les points au solde du compte de la plate-forme FMZ au ratio de 10: 1, et les utilisateurs peuvent également utiliser les points pour échanger les produits connexes de FMZ Quant à l'avenir. Lien spécifique pour l'activité:https://www.fmz.com/bbs-topic/3828

Plateforme quantique FMZ pour les entreprises

Le site Web complet de FMZ peut être déployé sur le serveur exclusif d'une entreprise ou d'une équipe pour un contrôle complet et une personnalisation fonctionnelle. Le site Web FMZ a été utilisé et testé par environ 100 000 utilisateurs et a atteint une disponibilité et une sécurité élevées, ce qui peut faire gagner du temps aux équipes et aux entreprises quantitatives. La version d'entreprise est destinée aux équipes de négociation quantitative de taille moyenne, aux fournisseurs de services de contrats à terme sur matières premières, etc. Veuillez contacter l'administrateur pour des devis spécifiques.

Système de création de marché

Le système professionnel, qui fournit la liquidité du marché et la gestion des fonds pour les plateformes, est peut-être le système de création de marché le plus amélioré du marché.

Système de plateforme

Le système de négociation de la technologie FMZ adopte la technologie de correspondance de mémoire, et la vitesse de traitement des commandes est aussi élevée que 2 millions de transactions par seconde, ce qui peut garantir qu'il n'y aura pas de retard ou de décalage dans le traitement des commandes. Il peut maintenir le fonctionnement en douceur et stable des plates-formes avec plus de 20 millions d'utilisateurs en ligne simultanés. Le cadre du système multi-couche et multi-cluster garantit la sécurité, la stabilité et l'extensibilité du système. Le déploiement de fonctions et les mises à jour de versions peuvent être effectuées sans temps d'arrêt, ce qui garantit au maximum l'expérience de fonctionnement des utilisateurs du terminal.


Plus de