[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.
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.Info
Le 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.
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)
}
Pour accéder aux interfaces publiques, Js peut utiliserHttpQuery
, et Python peut utiliser des paquets connexes, tels queurllib
ourequests
.
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()
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:
https://www.bitmex.com/api/explorer/
;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 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
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.
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;
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"]}')
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)
}
}
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,
Dans ce cas, il est évident que le simple fait d'utiliser
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
}
}
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
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)
}
}
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.
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
, GetAccount
et ainsi de suite.IO
est é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
À ce stade, le cadre de programmation global peut être écrit sous forme de modèle, similaire au modèle
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
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__isStock
FMZ 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
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
'''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()
exchange.SetData(arr) commute la source de données de backtest et utilise des données de ligne K personnalisées. Le paramètre
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
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.
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.
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
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
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 .
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
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
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.
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é.
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.