[TOC] Antes de aprender este tutorial, você precisa aprender.Introdução à plataforma de quantificação dos inventores da FMZeTutorial para iniciantes em estratégias de plataforma de quantificação FMZA partir de agora, o blogueiro vai se dedicando exclusivamente à sua área de atuação.O tutorial inicial aborda as funções mais usadas, mas há muitas outras funções e funcionalidades que não serão abordadas neste tutorial.A plataforma FMZ é apenas uma ferramenta para que você possa escrever estratégias mais livres e personalizadas.
A plataforma FMZ é encapsulada para todas as plataformas suportadas. Para manter a uniformidade, o suporte da API de uma única plataforma não é completo. Como obter K-lines geralmente pode transmitir o número de linhas K ou o tempo de início, enquanto a plataforma FMZ é fixa, algumas plataformas suportam pedidos em massa, a FMZ não é suportada, etc. Portanto, é necessário um método para acessar diretamente os dados da plataforma.Para interfaces abertas (se for o caso), pode-se usarHttpQueryPara acessar as informações da conta, é necessário usar o Google Analytics.IO。Os parâmetros de entrada específicos devem ser consultados no respectivo documento da API da bolsa.InfoO campo retorna a informação original, mas ainda não resolve o problema de não suportar a interface.
Retorna o conteúdo original do último pedido de REST API (string), que pode ser usado para partilhar a informação de extensão.
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)
}
Acesso à interface pública, Js pode ser usadoHttpQueryO Python pode usar os pacotes relacionados, como:urllibourequests。
O HttpQuery é o método GET por defeito, mas também suporta mais funcionalidades, veja a documentação da 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"))
Exemplos de requisições no 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()
Para interfaces que requerem assinatura API-KEY, pode-se usar a função IO, o usuário só precisa se preocupar com o parâmetro de entrada, o processo de assinatura específico será feito pelo nível inferior.
A plataforma FMZ atualmente não suporta o Stop Loss Order do BitMEX, que pode ser implementado através do IO seguindo os seguintes passos:
https://www.bitmex.com/api/explorer/。https://www.bitmex.com/api/v1/orderO método éPOSTComo o FMZ já especificou o endereço de raiz internamente, basta inserir “/api/v1/order”.symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=StopCódigo específico:
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"}))
Mais exemplos de IO: https://www.fmz.com/bbs-topic/3683
Basicamente, todas as exchanges de moeda digital suportam o envio de websocket, e algumas exchanges suportam a atualização de informações da conta do websocket. Em comparação com a rest API, a websocket geralmente possui baixa latencia, alta frequência e não é limitada pela frequência da plataforma rest API, o inconveniente é que há problemas de interrupção e o processamento não é intuitivo.
Este artigo irá abordar principalmente a plataforma de quantificação dos inventores da FMZ, usando a linguagem JavaScript, usando a função Dial embutida na plataforma para fazer conexões, com especificações e parâmetros no documento, pesquisando o Dial, que foi atualizado várias vezes para implementar várias funções. Este artigo irá cobrir isso e abordar a estratégia de direção de eventos baseada no wss, bem como a questão de conectar várias exchanges. O Python também pode usar a função Dial, e também pode usar a correspondente biblioteca.
Em geral, a conexão é direta, como o envio de ticker de segurança de moeda:
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
Para que os dados retornados sejam em formato de compressão, é necessário especificar o formato de compressão na conexão, comprimir especifica o formato de compressão, o modo representa o envio de dados retornados que precisam ser comprimidos, como a conexão OKEX:
var client = Dial("wss://real.okex.com:10441/websocket?compress=true|compress=gzip_raw&mode=recv")
A função Dial suporta a reconexão, feita pela linguagem Go de nível inferior, a detecção da conexão desligada e a reconexão, é conveniente e recomendada para o conteúdo de dados solicitados já no url, como o exemplo de Binance recentemente. Para aqueles que precisam enviar mensagens por encomenda, o mecanismo de reconexão pode ser mantido por conta própria.
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr|reconnect=true")
Para subscrever as mensagens do wss, algumas das exchanges pedem no url, e também há alguns canais que precisam enviar suas próprias assinaturas, como a coinbase:
client = Dial("wss://ws-feed.pro.coinbase.com", 60)
client.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')
Em geral, o websocket é usado para ler a situação, mas também pode ser usado para obter pedidos e transferência de contas. O envio de dados criptografados desse tipo às vezes tem um grande atraso, portanto, é necessário ter cuidado. Como o método de criptografia é mais complexo, alguns exemplos são dados aqui.
//火币期货推送例子
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)
}
}
O código é o seguinte:
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 data push é muito rápido, o nível de base do Go vai guardar todos os dados na fila, quando o programa é chamado de leitura, e depois retornar. Enquanto a operação de encomenda no disco rígido vai trazer um atraso, que pode causar acumulação de dados. Para transação push, conta push, profundidade inserção push, etc, nós precisamos de dados históricos, para o caso de dados, a maioria das vezes, só se preocupar com o mais recente, não se preocupar com dados históricos.
read()Se você não adicionar nenhum parâmetro, ele retornará o mais antigo dado, e se não tiver dados, ele será bloqueado. Se você quiser o mais recente, você pode usarclient.read(-2)Retornar os dados mais recentes imediatamente, mas retornar null quando não houver mais dados, necessitando de um julgamento de referência.
Dependendo de como tratar os dados antigos armazenados no cache, e se eles estão bloqueados quando não há dados, o read possui diferentes parâmetros, como o gráfico abaixo, que parece complicado, mas permite maior flexibilidade no programa.

Para este tipo de situação, o procedimento não pode ser simplesmente usado, pois uma bolsa bloqueia a mensagem em espera, enquanto a outra bolsa não recebe a mensagem, mesmo que haja uma nova. O procedimento geral é:
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
}
}
Esta parte do processamento é um pouco problemático, porque o envio de dados pode ser interrompido, ou o envio de atraso é muito alto, mesmo que possa receber o heartbeat não significa que os dados ainda estão sendo enviados, você pode configurar um intervalo de evento, se exceder o intervalo sem receber atualizações, re-conecte-se, e é melhor comparar o resultado com o rest retornado em um período de tempo para ver se os dados estão corretos. Para este caso especial, o envio de moeda pode ser configurado diretamente.
Uma vez que os dados de push já foram usados, o programa também deve ser escrito como um driver de eventos, observe que os dados de push são frequentes e não são bloqueados com muitas solicitações, geralmente pode ser escrito como:
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)
}
}
O modo de conexão, o modo de envio de dados, o conteúdo de assinatura e o formato de dados de cada plataforma geralmente são diferentes, então a plataforma não é encapsulada e precisa se conectar automaticamente com a função Dial. Este artigo abrange basicamente alguns cuidados básicos.
PS. Algumas casas de câmbio não oferecem websocket, mas quando você acessa o site e usa a função de modelagem, você descobre que eles usam o websocket push. Se você pesquisar, você encontrará o formato de assinatura e o formato de retorno. Algumas parecem criptografadas e podem ser decodificadas com base64.
O JavaScript pode ser implementado em paralelo através da função Go, e o Python pode usar a correspondente biblioteca multi-thread.
Na implementação de estratégias de quantificação, em muitos casos, a execução em simultâneo pode reduzir a eficiência do aumento do tempo de atraso. Por exemplo, o disco rígido da estratégia de hedge precisa obter a profundidade de duas moedas, e o código executado na sequência é o seguinte:
var depthA = exchanges[0].GetDepth()
var depthB = exchanges[1].GetDepth()
Há um atraso na solicitação de uma API de descanso. Suponha que seja 100 ms. Então o tempo para obter a profundidade duas vezes é realmente diferente. Se mais acesso for necessário, o problema de atraso será mais proeminente, afetando a execução da estratégia.
Como o JavaScript não possui multithreads, o substrato embala a função Go para resolver este problema. A função Go pode ser usada em APIs que requerem acesso à rede, comoGetDepth,GetAccountE assim por diante.IOA chamada pode ser:exchange.Go("IO", "api", "POST", "/api/v1/contract_batchorder", "orders_data=" + JSON.stringify(orders))Mas, devido ao mecanismo de projeto, a implementação é um pouco complicada.
var a = exchanges[0].Go("GetDepth")
var b = exchanges[1].Go("GetDepth")
var depthA = a.wait() //调用wait方法等待返回异步获取depth结果
var depthB = b.wait()
Na maioria dos casos simples, não há nada de errado em escrever a política dessa maneira. Mas observe que esse processo deve ser repetido toda vez que a estratégia for executada em loop, e as variáveis intermediárias a e b são, na verdade, apenas auxílios temporários. Se tivermos muitas tarefas simultâneas, precisamos registrar a correspondência entre a e depthA, e b e depthB. Quando nossas tarefas simultâneas são incertas, a situação se torna mais complicada. Portanto, queremos implementar uma função: ao escrever simultaneidade Go, vincular uma variável ao mesmo tempo e, quando o resultado da execução simultânea for retornado, o resultado será automaticamente atribuído à variável, eliminando assim as variáveis intermediárias e tornando o programa mais conciso. A implementação específica é a seguinte:
function G(t, ctx, f) {
return {run:function(){
f(t.wait(1000), ctx)
}}
}
Definimos uma função G, onde o parâmetro t é a função Go a ser executada, ctx é o contexto do programa e f é a função para atribuição específica. Veremos essa função em ação em breve.
Neste ponto, a estrutura geral do programa pode ser escrita como um modelo “produtor-consumidor” (com algumas diferenças), onde os produtores emitem tarefas continuamente e os consumidores as executam concorrentemente. O código a seguir é apenas para demonstração e não envolve o programa. Execute a lógica.
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)
}
}
Parece que implementamos apenas uma função simples depois de passar por muitas etapas, mas, na verdade, a complexidade do código foi bastante simplificada. Precisamos apenas nos preocupar com quais tarefas o programa precisa gerar e o worker( ) o programa irá executá-los automaticamente simultaneamente e retornar os resultados correspondentes. A flexibilidade foi bastante melhorada.
O gráfico é uma biblioteca de gráficos recomendada e, na maioria dos casos, pode atender às necessidades. Se precisar de mais personalização, pode operar diretamente o objeto de gráfico.
Chart({…})Os parâmetros internos são os objetos HighStock e HighCharts, apenas um parâmetro adicional foi adicionado__isStockO HighStock é mais focado em gráficos de sequência de tempo e, portanto, é mais comum. O FMZ suporta os módulos básicos do HighCharts e do HighStock, mas não os módulos adicionais.
Exemplos específicos de HighCharts: https://www.highcharts.com/demo ; Exemplos de HighStock: https://www.highcharts.com/stock/demo 。 O código desses exemplos pode ser facilmente transportado para o FMZ。
Pode ser chamado de add ().[series index ((como 0), data)) adicionar dados a series especificada no índice, chamar reset ((() limpar os dados do gráfico, reset pode ter um parâmetro numérico, especificar o número de itens reservados. Suporta a exibição de vários gráficos, quando configurado, basta passar para o parâmetro do array, como: var chart = Chart ((([{…}, {…}, {…}]), por exemplo, gráfico um tem duas séries, gráfico dois tem uma série, gráfico três tem uma série, então quando adicionar especificar 0 e 1 sequência ID representa a atualização de gráfico 1 de duas séries de dados, quando adicionar especificar sequência ID 2 refere-se a dados da primeira série de gráfico 2 e especificar a sequência 3 refere-se a dados da primeira série de gráfico 3.
Um exemplo concreto:
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);
}
}
Um exemplo de um layout gráfico: https://www.fmz.com/strategy/136056
O endereço de código aberto específico é: https://github.com/fmzquant/backtest_python
Instalação
Digite o seguinte na linha de comando:
pip install https://github.com/fmzquant/backtest_python/archive/master.zip
Exemplos simples
Os parâmetros de retorno são definidos como comentários no início do código de política. Para mais informações, consulte a interface de edição de política do site FMZ para salvar as configurações de retorno.
'''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
Testes retrospectivos
Uma vez que a estratégia completa requer um ciclo de morte, uma exceção de EOF será lançada para encerrar o procedimento após o término do feedback, portanto, é necessário ter tolerância a erros.
# !/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), comuta a fonte de dados de ressonância, usando os dados de linha K que são definidos. O parâmetro arr, que é um conjunto de dados de coluna de linha K, é um conjunto de dados de linha K que, temporariamente, só suporta ressonância em JavaScript.
No array arr, o formato de dados de cada elemento é:
[
1530460800, // time 时间戳
2841.5795, // open 开盘价
2845.6801, // high 最高价
2756.815, // low 最低价
2775.557, // close 收盘价
137035034 // volume 成交量
]
As fontes de dados podem ser importadas para o repositório de classes de templates.
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("导入数据成功")
}
Observação: é necessário que, na inicialização, os dados personalizados sejam importados primeiro (ou seja, os dados de configuração da função exchange.SetData são chamados), o ciclo de dados de linha K definido deve coincidir com o ciclo de linha K de base definido na página de ressonância, ou seja, os dados de linha K personalizados, o tempo de uma linha K é de 1 minuto, então o ciclo de linha K de base definido na ressonância também deve ser definido como 1 minuto.
Se a plataforma não suportada e a plataforma suportada tiverem a mesma API, apenas um endereço de base diferente, pode ser suportada por meio de uma troca de endereço de base. Especificamente, ao adicionar a plataforma, selecione a plataforma suportada, mas preencha o API-KEY da plataforma não suportada, use o endereço de base do IO na estratégia, como:
exchange.IO("base", "http://api.huobi.pro")
//http://api.huobi.pro为为支持交易所API基地址,注意不用添加/api/v3之类的,会自动补全
Nem todas as exchanges suportam o FMZ, mas a plataforma oferece acesso a um protocolo genérico.
Em termos simples, o protocolo geral equivale a um intermediário, que representa o pedido do host e retorna os dados de acordo com os padrões correspondentes. O código do protocolo geral precisa ser feito por si mesmo, e escrever o protocolo geral na verdade representa que você pode acessar a bolsa separadamente e concluir a estratégia.
O acordo foi assinado pelo presidente da Federação das Indústrias do Brasil (FIEP) e o presidente da Federação das Indústrias do Brasil (FIEP) e o presidente da Federação das Indústrias do Brasil (FIEP). Exemplo de um protocolo genérico escrito em Python: https://www.fmz.com/strategy/101399
Assim como as operações da bolsa podem ser realizadas através da API, o site da FMZ também é baseado em API. Você pode solicitar o seu próprio API-KEY do site da FMZ para implementar funções como criação, reinicialização, eliminação de disco rígido, acesso a uma lista de discos rígidos e acesso a um diário de discos rígidos.
Devido à forte escalabilidade da plataforma FMZ, você pode criar sua própria plataforma de quantificação com base na API de extensão, permitindo que os usuários executem o disco rígido em sua plataforma, etc.
O mercado de negociação de moedas digitais está recebendo cada vez mais atenção dos comerciantes de quantificação por causa de sua peculiaridade. Na verdade, a negociação programada já é a principal corrente da moeda digital, e estratégias como o mercado de cobertura estão sempre ativas no mercado.
DivulgaçãoCurso de negociação quantitativa de moedas digitais no NetEase Cloud│ acessar o curso de NetEase Cloud e compartilhar o link do seu curso (o link traz um curso ID exclusivo), os outros registrar e comprar o curso através deste link, você vai receber 50% de 10 yuan. │ atenção para a promoção do curso de buffet do curso de NetEase Cloud e o número público do WeChat estão disponíveis.
O consumidor clica no link de promoção e, dentro de seis meses, registra-se e recebe o valor, a empresa reembolsa a comissão de acordo com o valor efetivo do pedido válido. A comissão será devolvida na forma de pontos na conta do promotor, o usuário pode trocar o inventor quantificando o saldo da conta da plataforma de negociação em uma proporção de 10:1, ou pode trocar o inventor quantificando o valor dos produtos ao redor mais tarde.
O site FMZ completo pode ser implantado no servidor exclusivo da empresa ou da equipe, permitindo o controle completo e a personalização das funções. O site FMZ foi usado e testado por cerca de 100 mil usuários, alcançando alta disponibilidade e segurança, economizando tempo para a equipe de quantificação e a empresa. A versão corporativa é voltada para equipes de negociação de quantificação de médio porte, fornecedores de serviços de futuros de mercadorias, etc.
O sistema profissional para fornecer liquidez de mercado e gestão de fundos para as exchanges é provavelmente o sistema de negociação mais completo do mercado e é usado por muitas exchanges e equipes.
O sistema de negociação de tecnologia do inventor usa a tecnologia de fusão de memória, a velocidade de processamento de pedidos é de até 2 milhões de pips / segundo, o que garante que o processamento de pedidos não ocorra nenhum atraso e cartão. Pode manter o funcionamento estável e fluido de mais de 20 milhões de exchanges com usuários on-line ao mesmo tempo. A arquitetura de sistema de múltiplos níveis e clusters garante a segurança, a estabilidade e a facilidade de expansão do sistema.