[TOC] Il est nécessaire d’apprendre avant d’apprendre ce tutorielDébut de la plateforme de quantification des inventeurs de FMZetLa stratégie de la plateforme de quantification FMZIl a également écrit des articles sur le sujet et sur la façon dont il utilise le langage de programmation.Le tutoriel de base traite des fonctions les plus couramment utilisées, mais il y a beaucoup d’autres fonctions et fonctionnalités qui ne sont pas présentées et qui ne seront pas couvertes dans ce tutoriel. Consultez la documentation de l’API de la plateforme pour en savoir plus.Une fois ce tutoriel terminé, vous serez en mesure d’écrire des stratégies plus libres et plus personnalisées, et la plateforme FMZ n’est qu’un outil.
La plate-forme FMZ est encapsulée pour toutes les plateformes supportées. Pour maintenir l’uniformité, la prise en charge de l’API d’une seule plate-forme n’est pas complète. Si l’acquisition de lignes K peut généralement être transmise dans le nombre de lignes K ou l’heure de début, alors que la plate-forme FMZ est fixe, certaines plates-formes prennent en charge les commandes en gros, FMZ n’est pas pris en charge, etc.Pour les interfaces publiques (si cela est possible), on peut utiliserHttpQueryPour accéder aux informations de votre compte, vous devez utiliser:IO。Pour les paramètres d’entrée spécifiques, consultez la documentation de l’API de l’échange.InfoLe champ renvoie l’information initiale, mais ne parvient toujours pas à résoudre le problème de l’interface non prise en charge.
Retourne le contenu original de la dernière requête de l’API REST (string) qui peut être utilisé pour analyser les informations d’extension.
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)
}
Accès à l’interface publique, Js est disponibleHttpQueryPython peut utiliser des packages commeurllibourequests。
HttpQuery est la méthode GET par défaut, mais il prend en charge d’autres fonctionnalités, voir la documentation de l’API.
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 d’utilisation des requêtes par Python
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 nécessitant une signature API-KEY, la fonction IO peut être utilisée, l’utilisateur n’a qu’à se soucier des paramètres de transmission, le processus de signature spécifique sera effectué par le niveau inférieur.
La plate-forme FMZ ne prend pas en charge les ordres stop-loss BitMEX, mais les étapes suivantes sont à suivre pour les mettre en œuvre via IO:
https://www.bitmex.com/api/explorer/。https://www.bitmex.com/api/v1/orderLa méthode estPOSTComme FMZ a déjà spécifié l’adresse racine en interne, il suffit de saisir “/api/v1/order”.symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=StopLe code est:
var id = exchange.IO("api", "POST", "/api/v1/order", "symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=Stop")
//也可以直接传入对象
var id = exchange.IO("api", "POST", "/api/v1/order", "", JSON.stringify({symbol:"XBTUSD",side:"Buy",orderQty:1,stopPx:4000,ordType:"Stop"}))
Pour plus d’exemples d’IO:
La plupart des plateformes de crypto-monnaie prennent en charge le transfert de données par websocket, et certaines d’entre elles prennent en charge la mise à jour des informations de compte par websocket. Comparé à l’API de repos, websocket a généralement un faible temps de latence, une fréquence élevée et n’est pas limité par la fréquence de l’API de repos de la plate-forme.
Cet article traitera principalement de la plate-forme de quantification des inventeurs de FMZ, utilisant le langage JavaScript, la fonction Dial intégrée à la plate-forme pour la connexion, les spécifications et les paramètres sont dans la documentation, la recherche de Dial, la fonction Dial a été mise à jour plusieurs fois pour implémenter diverses fonctionnalités, cet article couvrira cela et présentera les stratégies d’actionnement des événements basées sur wss, ainsi que les problèmes de connexion multi-échanges.
Il existe des connexions directes, telles que la possibilité d’obtenir des tickers de sécurité:
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
Pour que les données retournées soient en format compressé, il faut que le format de compression soit spécifié lors de la connexion, compresser spécifie le format de compression, et le mode représente l’envoi de données retournées qui nécessite une compression, comme pour la connexion 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, réalisée par le langage Go sous-jacent, la reconnexion de la session de déconnexion détectée, est pratique et recommandée pour les contenus de données demandées déjà dans l’url, comme dans l’exemple de Binance. Pour ceux qui ont besoin d’envoyer des messages commandés, vous pouvez maintenir vous-même le mécanisme de reconnexion.
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr|reconnect=true")
Pour s’abonner aux nouvelles de wss, certaines bourses demandent des urls, et d’autres ont besoin de s’abonner à leurs propres chaînes, comme Coinbase:
client = Dial("wss://ws-feed.pro.coinbase.com", 60)
client.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')
Il est courant d’utiliser un websocket pour lire les données, mais il peut également être utilisé pour obtenir des commandes et des comptes. Le transfert de ces données cryptées peut parfois être très retardé, il faut donc être prudent.
//火币期货推送例子
var ACCESSKEYID = '你的火币账户的accesskey'
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} }") //去掉}}之间的多余空格
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}))
}
}
//币安推送例子,注意需要定时更新listenKey
var APIKEY = '你的币安accesskey'
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()
}
//BitMEX推送例子
var APIKEY = "你的Bitmex API ID"
var expires = parseInt(Date.now() / 1000) + 10
var signature = exchange.HMAC("sha256", "hex", "GET/realtime" + expires, "{{secretkey} }")//secretkey在执行时自动替换,不用填写
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)
}
}
Les codes suivants sont généralement lus en continu dans la boucle de mort:
function main() {
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr");
while (true) {
var msg = client.read()
var data = JSON.parse(msg) //把json字符串解析为可引用的object
// 处理data数据
}
}
wss est très rapide, Go cache toutes les données dans une queue, et les programmes comme read sont appelés pour les renvoyer ensuite. Les commandes sur le disque dur entraînent des retards, ce qui peut entraîner une accumulation de données. Pour le transfert de transactions, le transfert de comptes, le transfert de valeurs en profondeur, etc., nous avons besoin de données historiques, et pour les données de situation, nous ne nous soucions que des données les plus récentes, pas des données historiques.
read()Si vous n’ajoutez pas de paramètres, vous renvoyez les données les plus anciennes, et si vous n’avez pas de données, vous les bloquez. Si vous voulez les données les plus récentes, vous pouvez utiliserclient.read(-2)Il faut retourner immédiatement les données les plus récentes, mais retourner null quand il n’y a plus de données, il faut juger et référencer.
Les paramètres de read varient en fonction de la manière dont les données anciennes sont traitées dans la cache et si elles sont bloquées en l’absence de données.

Il est évident qu’il n’est pas possible d’utiliser un simple read () dans ce cas, car un échange bloquerait les messages en attente, tandis qu’un autre échange ne recevrait pas de nouveaux messages même s’il y en avait. Le traitement général est le suivant:
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) // 参数-1代表无数据立即返回null,不会阻塞到有数据返回
var msgCoinbase = coinbase.read(-1)
if(msgBinance){
// 此时币安有数据返回
}
if(msgCoinbase){
// 此时coinbase有数据返回
}
Sleep(1) // 可以休眠1ms
}
}
Cette partie du traitement est un peu gênante, car les données de poussée peuvent être interrompues, ou le retard de poussée est très élevé, même si la réception de heartbeat ne signifie pas que les données sont toujours en cours de poussée, vous pouvez définir un intervalle d’événement, si vous ne recevez pas de mises à jour après l’intervalle, vous pouvez vous reconnecter, et il est préférable de comparer les résultats avec le retour de rest après un certain temps pour voir si les données sont exactes.
Étant donné que les données de poussée sont déjà utilisées, les programmes doivent naturellement être écrits comme des événements, en tenant compte du fait que les données de poussée sont fréquentes et ne sont pas bloquées par trop de requêtes. On peut généralement écrire:
var tradeTime = Date.now()
var accountTime = Date.now()
function trade(data){
if(Date.now() - tradeTime > 2000){//这里即限制了2s内只交易一次
tradeTime = Date.now()
//交易逻辑
}
}
function GetAccount(){
if(Date.now() - accountTime > 5000){//这里即限制了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)
}
}
La façon dont les websockets de chaque plateforme sont connectés, les données envoyées, le contenu souscrit et le format de données sont souvent différents, de sorte que la plateforme n’est pas encapsulée et nécessite une connexion automatique à l’aide de la fonction Dial. Cet article couvre essentiellement quelques précautions de base.
PS. Certaines bourses n’offrent pas de websocket, mais si vous vous connectez à leur site, vous verrez qu’elles utilisent le websocket push, et que vous trouverez des formats de souscription et de retour. Certaines semblent avoir été cryptées et peuvent être décodées en base64.
JavaScript peut être parallélisé par la fonction Go, et Python peut utiliser la bibliothèque multi-threading correspondante.
Dans la mise en œuvre d’une stratégie quantifiée, dans de nombreux cas, l’exécution simultanée peut réduire l’efficacité de l’amélioration de la latence. Par exemple, pour le disque dur d’une stratégie de couverture, il est nécessaire d’obtenir la profondeur de deux pièces, le code exécuté dans l’ordre suivant:
var depthA = exchanges[0].GetDepth()
var depthB = exchanges[1].GetDepth()
Il y a un délai dans la demande d’une API REST. Supposons qu’il soit de 100 ms. Le temps nécessaire pour obtenir la profondeur deux fois est alors en réalité différent. Si un accès plus important est requis, le problème de délai sera plus important, affectant l’exécution de la stratégie.
Comme il n’y a pas de multi-threading, le JavaScript a encapsulé la fonction Go pour résoudre ce problème. La fonction Go peut être utilisée pour les API nécessitant un accès réseau, commeGetDepth,GetAccountEt ainsi de suite.IOPar exemple:exchange.Go("IO", "api", "POST", "/api/v1/contract_batchorder", "orders_data=" + JSON.stringify(orders))Mais le mécanisme de conception rend la mise en œuvre plus difficile.
var a = exchanges[0].Go("GetDepth")
var b = exchanges[1].Go("GetDepth")
var depthA = a.wait() //调用wait方法等待返回异步获取depth结果
var depthB = b.wait()
Dans la plupart des cas simples, il n’y a rien de mal à rédiger la politique de cette façon. Mais notez que ce processus doit être répété à chaque fois que la stratégie boucle, et les variables intermédiaires a et b ne sont en fait que des aides temporaires. Si nous avons beaucoup de tâches simultanées, nous devons enregistrer la correspondance entre a et profondeur A, et b et profondeur B. Lorsque nos tâches simultanées sont incertaines, la situation devient plus compliquée. Par conséquent, nous voulons implémenter une fonction : lors de l’écriture de la concurrence Go, lier une variable en même temps, et lorsque le résultat de l’exécution simultanée est renvoyé, le résultat est automatiquement attribué à la variable, éliminant ainsi les variables intermédiaires et rendant le programme plus concis. L’implémentation spécifique est la suivante :
function G(t, ctx, f) {
return {run:function(){
f(t.wait(1000), ctx)
}}
}
Nous définissons une fonction G, où le paramètre t est la fonction Go à exécuter, ctx est le contexte du programme et f est la fonction d’affectation spécifique. Nous verrons cette fonction en action sous peu.
À ce stade, le cadre global du programme peut être écrit sous la forme d’un modèle « producteur-consommateur » (avec quelques différences). Le producteur émet en permanence des tâches et le consommateur les exécute simultanément. Le code suivant est uniquement destiné à la démonstration et n’implique pas le programme. Exécuter la logique.
var Info = [{depth:null, account:null}, {depth:null, account:null}] //加入我们需要获取两个交易所的深度和账户,跟多的信息也可以放入,如订单Id,状态等。
var tasks = [ ] //全局的任务列表
function produce(){ //下发各种并发任务
//这里省略了任务产生的逻辑,仅为演示
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 //这里的v就是并发Go函数wait()的返回值,可以仔细体会下
}))
}
_.each(jobs, function(t){
t.run() //在这里并发执行所有任务
})
tasks = []
}
function main() {
while(true){
produce() // 发出交易指令
worker() // 并发执行
Sleep(1000)
}
}
Il semble que nous n’ayons implémenté qu’une fonction simple après avoir suivi de nombreuses étapes, mais en fait, la complexité du code a été grandement simplifiée. Nous devons seulement nous soucier des tâches que le programme doit générer et du worker( ) le programme les exécutera automatiquement simultanément et renverra les résultats correspondants. La flexibilité a été grandement améliorée.
Le tutoriel de base sur les diagrammes est une bibliothèque de diagrammes recommandée, qui peut répondre à la plupart des besoins. Si vous avez besoin d’une personnalisation plus poussée, vous pouvez utiliser directement les objets de diagramme.
Chart({…})Les paramètres internes sont les objets HighStock et HighCharts, mais un paramètre supplémentaire a été ajouté.__isStockPour distinguer HighStock des autres modules, il faut savoir que HighStock se concentre davantage sur les graphiques en séquence temporelle et est donc plus utilisé. FMZ prend en charge les modules de base de HighCharts et HighStock, mais ne prend pas en charge les modules supplémentaires.
Le code de ces exemples peut être facilement porté sur FMZ.
On peut appeler add ().[series index ((comme 0), données]) ajoute des données à une série spécifiée, appelle reset (() vide les données du graphique, reset peut être associé à un paramètre numérique, spécifie le nombre d’articles réservés. Supporte l’affichage de plusieurs graphiques, la configuration ne nécessite que la transmission de paramètres d’archive, comme: var chart = Chart (([{…}, {…}, {…}]), par exemple le graphique un a deux séries, le graphique deux a une série, le graphique trois a une série, alors add indique que les ID de séquence 0 et 1 représentent les données des deux séquences de mise à jour du graphique 1, add indique que le ID de séquence 2 représente les données de la première série du graphique 2, et indique que la séquence 3 représente les données de la première série du graphique 3.
Un exemple concret:
var chart = { // 这个 chart 在JS 语言中 是对象, 在使用Chart 函数之前我们需要声明一个配置图表的对象变量chart。
__isStock: true, // 标记是否为一般图表,有兴趣的可以改成 false 运行看看。
tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'}, // 缩放工具
title : { text : '差价分析图'}, // 标题
rangeSelector: { // 选择范围
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'}, // 坐标轴横轴 即:x轴, 当前设置的类型是 :时间
yAxis : { // 坐标轴纵轴 即:y轴, 默认数值随数据大小调整。
title: {text: '差价'}, // 标题
opposite: false, // 是否启用右边纵轴
},
series : [ // 数据系列,该属性保存的是 各个 数据系列(线, K线图, 标签等..)
{name : "line1", id : "线1,buy1Price", data : []}, // 索引为0, data 数组内存放的是该索引系列的 数据
{name : "line2", id : "线2,lastPrice", dashStyle : 'shortdash', data : []}, // 索引为1,设置了dashStyle : 'shortdash' 即:设置 虚线。
]
};
function main(){
var ObjChart = Chart(chart); // 调用 Chart 函数,初始化 图表。
ObjChart.reset(); // 清空
while(true){
var nowTime = new Date().getTime(); // 获取本次轮询的 时间戳, 即一个 毫秒 的时间戳。用来确定写入到图表的X轴的位置。
var ticker = _C(exchange.GetTicker); // 获取行情数据
var buy1Price = ticker.Buy; // 从行情数据的返回值取得 买一价
var lastPrice = ticker.Last + 1; // 取得最后成交价,为了2条线不重合在一起 ,我们加1
ObjChart.add([0, [nowTime, buy1Price]]); // 用时间戳作为X值, 买一价 作为Y值 传入 索引0 的数据序列。
ObjChart.add([1, [nowTime, lastPrice]]); // 同上。
Sleep(2000);
}
}
Un exemple d’utilisation de la mise en page graphique: https://www.fmz.com/strategy/136056
L’adresse exacte de l’ouverture est à: https://github.com/fmzquant/backtest_python
Mise en place
Entrez la commande suivante à la ligne de commande:
pip install https://github.com/fmzquant/backtest_python/archive/master.zip
Des exemples simples
Les paramètres de rétroaction sont définis en commentaires au début du code de la stratégie.
'''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
Backtesting
Comme une stratégie complète nécessite une boucle morte, une exception d’EOF est lancée à la fin de la rétroanalyse pour mettre fin à la procédure, il est donc nécessaire de faire une tolérance aux erreurs.
# !/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__
# ------------------------------ 策略部分开始 --------------------------
print exchange.GetAccount() # 调用一些接口,打印其返回值。
print exchange.GetTicker()
def adjustFloat(v): # 策略中自定义的函数
v = math.floor(v * 1000)
return v / 1000
def onTick():
Log("onTick")
# 具体的策略代码
def main():
InitAccount = GetAccount()
while True:
onTick()
Sleep(1000)
# ------------------------------ 策略部分结束 --------------------------
try:
main() # 回测结束时会 raise EOFError() 抛出异常,来停止回测的循环。所以要对这个异常处理,在检测到抛出的异常后调用 task.Join() 打印回测结果。
except:
print task.Join()
exchange.SetData(arr), commutation de la source de données de rétroaction, avec les données de ligne K personnalisées. Le paramètre arr, un ensemble de données de ligne K composé d’éléments de colonne (((c’est-à-dire un ensemble de données de ligne K, ne prend actuellement en charge que la rétroaction JavaScript。
Le format de données des éléments individuels dans un tableau arr est:
[
1530460800, // time 时间戳
2841.5795, // open 开盘价
2845.6801, // high 最高价
2756.815, // low 最低价
2775.557, // close 收盘价
137035034 // volume 成交量
]
Les sources de données peuvent être importées dans les référentiels de la classe de modèle.
function init() { // 模板中的 init 初始化函数会在加载模板时,首先执行,确保 exchange.SetData(arr) 函数先执行,初始化,设置数据给回测系统。
var arr = [ // 回测的时候需要使用的K线数据
[1530460800,2841.5795,2845.6801,2756.815,2775.557,137035034], // 时间最早的一根 K线柱 数据
... , // K线数据太长,用 ... 表示,数据此处省略。
[1542556800,2681.8988,2703.5116,2674.1781,2703.5116,231662827] // 时间最近的一根 K线柱 数据
]
exchange.SetData(arr) // 导入上述 自定义的数据
Log("导入数据成功")
}
Remarque: lors de l’initialisation, il est nécessaire d’importer d’abord les données personnalisées (c’est-à-dire d’appeler les données de configuration de la fonction exchange.SetData), la période de données de ligne K personnalisée doit être identique à la période de ligne K sous-jacente définie sur la page de réévaluation, c’est-à-dire: les données de ligne K personnalisées, la durée d’une ligne K est de 1 minute, alors la période de ligne K sous-jacente définie dans la réévaluation doit également être de 1 minute.
Si l’API de l’échange non pris en charge est exactement la même que celle de l’échange pris en charge, mais que l’adresse de base est différente, il est possible de le prendre en charge en changeant l’adresse de base. Pour ajouter un échange spécifique, sélectionnez l’échange pris en charge, mais remplissez l’API-KEY pour l’échange non pris en charge, changez l’adresse de base avec IO dans la stratégie, comme:
exchange.IO("base", "http://api.huobi.pro")
//http://api.huobi.pro为为支持交易所API基地址,注意不用添加/api/v3之类的,会自动补全
Les FMZ ne sont pas pris en charge par tous les échanges, mais la plateforme offre un accès aux protocoles généraux.
En termes simples, un protocole général est l’équivalent d’un intermédiaire qui représente la requête d’un hôte et renvoie les données conformément aux normes correspondantes. Le code du protocole général doit être complété par vous-même, l’écriture du protocole général représente en fait que vous pouvez accéder à l’échange séparément et terminer la stratégie.
Le projet de loi a été approuvé par le Parlement européen et le Conseil de l’Europe. Voici un exemple de protocole générique écrit en Python:
Toutes les opérations peuvent être réalisées par l’intermédiaire de l’API, tout comme les opérations de l’échange. Le site Web de FMZ est également basé sur l’API. Vous pouvez demander votre propre API-KEY du site Web de FMZ pour implémenter diverses fonctions telles que la création, le redémarrage, la suppression de disques réels, l’obtention d’une liste de disques réels et l’obtention d’un journal de disques réels.
Grâce à la forte extensibilité de la plate-forme FMZ, vous pouvez créer votre propre plate-forme de quantification basée sur l’API d’extension, permettre aux utilisateurs d’exécuter des disques fixes sur votre plate-forme, etc.
Le marché de la monnaie numérique est de plus en plus pris en compte par les traders quantifiés en raison de sa spécificité. En fait, la négociation programmatique est déjà le courant dominant de la monnaie numérique, et des stratégies telles que le marché de couverture sont toujours actives sur le marché. Les débutants qui ont une base de programmation faible qui veulent entrer dans ce domaine sont confrontés à de nombreux échanges et des API variables.
DiffusionLe cours sur la quantification des monnaies numériques de NetEase Cloud❚ Connectez-vous à NetEasy Cloud Classroom, partagez votre lien de cours (le lien contient un coursId unique), les autres personnes qui s’inscrivent et achètent le cours via ce lien, vous recevrez 50% du total de 10 yuans. ❚ Attention à la promotion des cours de boutique de NetEasy Cloud Classroom Le numéro public de Weibo est disponible ❚ Vous êtes invités à inviter d’autres personnes à promouvoir sur le groupe QQ de Weibo.
Le consommateur clique sur le lien de promotion et s’enregistre pour être remboursé dans les six mois. La commission est remboursée en fonction du montant effectif de la commande valide. La commission est remboursée sous forme de points sur le compte du promoteur.
L’intégralité du site FMZ peut être déployée sur le serveur exclusif de l’entreprise ou de l’équipe, ce qui permet un contrôle et une personnalisation complets des fonctions. Le site FMZ a été utilisé et testé par environ 100 000 utilisateurs et a atteint un niveau élevé de disponibilité et de sécurité, ce qui permet d’économiser du temps pour les équipes de quantification et les entreprises.
Un système professionnel de liquidité et de gestion de fonds pour les échanges est probablement le système de négociation le plus complet sur le marché, utilisé par de nombreuses échanges et équipes.
Le système de négociation technologique de l’inventeur utilise la technologie de fusion de mémoire, la vitesse de traitement des commandes atteint 2 millions de pièces / seconde, ce qui garantit que le traitement des commandes ne subira aucun retard et carton. Il peut maintenir le fonctionnement stable et fluide de plus de 20 millions d’échanges en ligne simultanément. L’architecture du système à plusieurs niveaux et groupes garantit la sécurité, la stabilité et la facilité d’extension du système. Le déploiement des fonctions et les mises à jour des versions ne nécessitent pas d’arrêt, garantissant au maximum l’expérience d’utilisation des utilisateurs finaux.