[TOC]
Este tutorial irá cobrir mais detalhes sobre a plataforma FMZ, mais habilidades práticas sobre o uso da API.
Depois de aprender todo o tutorial, você vai fazer pleno uso do FMZ e ser capaz de escrever estratégias mais personalizadas, mais eficientes e mais complexas.
Você pode negociar em várias bolsas e vários símbolos dentro de um robô facilmente.
exchange.GetTicker()
quando uma troca é adicionadaexchanges[0].GetTicker()
, exchanges[1].GetTicker()
exchange
através da utilizaçãoIO
funçãovar symbols = ["BTC_USDT", "LTC_USDT", "EOS_USDT", "ETH_USDT", "BCC_USDT"]
var buyValue = 1000
function main(){
for(var i=0;i<symbols.length;i++){
exchange.IO("currency", symbols[i]) // It is always valid until the next change
var ticker = exchange.GetTicker()
var amount = _N(buyValue/ticker.Sell, 3)
exchange.Buy(ticker.Sell, amount)
Sleep(1000)
}
}
Até agora, a FMZ suporta todas as principais bolsas de futuros, como a OKEX, HuobiDM, BitMEX, GateIO e Deribit, e seus contratos de swap.
Para negociar futuros no FMZ, você precisa adicionar uma troca de futuros primeiro, definir o símbolo quando iniciar o bot e definir o tipo de contrato em seu código.
Se uma bolsa oferecer apoio tanto ao mercado spot como ao mercado futuros, estes devem ser adicionados à FMZ separadamente.
A imagem abaixo mostra como definir o símbolo de futuros para BTC ao iniciar o bot.
Abaixo está como definir um tipo de contrato para cada troca.
exchange.SetContractType("swap")
exchange.SetContractType("this_week")
exchange.SetContractType("next_week")
exchange.SetContractType("quarter")
exchange.SetContractType("this_week")
exchange.SetContractType("next_week")
exchange.SetContractType("quarter")
exchange.SetContractType("XBTUSD")
exchange.SetContractType("XBTM19")
exchange.SetContractType("swap")
exchange.SetContractType("BTC-PERPETUAL")
exchange.SetContractType("BTC-27APR18")
Introdução básica
O FMZ tem dois modos de backtesting:real tick
esimulate tick
. O nível de tick real contém todos os dados históricos concluídos (um tick por segundo), de modo que os resultados de backtesting são mais confiáveis. O nível de simulação usa os dados de clones de histórico no intervalo usado pela sua estratégia. Os ticks dentro de um kline são gerados por um algoritmo que é o mesmo que o MT4, você pode encontrar mais detalhes emhttps://www.mql5.com/en/articles/75Enquanto isso, um intervalo mais curto pode ser escolhido como base-klines para gerar carrapatos.
O modo de simulação de tick é muito mais rápido, mas menos preciso do que o modo de simulação real.
Configuração de teste de retorno
Aqui estão as configurações padrão:Pedaços escondidos:
Resultado do teste de regresso
Ao chamar quaisquer funções que acessam a API de troca (comoGetTicker
, Buy
, CancelOrder
, etc...), você pode obter falha de acesso devido a um problema do servidor de troca, parâmetros errados, problema de transmissão de rede, e assim por diante.null
Então você precisa saber como lidar com erros.
Qual é o erro?
O bot vai retornar uma mensagem de erro quando um erro ocorre. Basta pesquisar o nome de troca + erro msg, você pode encontrar o que é o problema. Por exemplo, Um erro{"result":false,"error_code":20049}
é devolvido quando chamadaexchange.GetAccount()
no OKEX.OKEX 20049
, eis o resultado:Você também pode verificar o código de erro no documento do exchange API, tais comoCódigo de erro OKEX para futuros
Erros de negociação
Você deve considerar como lidar com erros ao escrever o código de estratégia.
// 1.Deal when the result is null
var ticker = exchange.GetTicker()
while(ticker == null){
Log('GetTicker error')
Sleep(100)
ticker = exchange.GetTicker()
}
Log(ticker.Last);
// 2.Refer when the result is not null
var ticker = exchange.GetTicker()
if(!ticker){
Log(ticker.Last)
}
// 3._C() fucntion retry
var ticker = _C(exchange.GetTicker) // can't apply _C to CancelOrder, Why?
Log(ticker.Last)
// 4. try catch
try{
var ticker = exchange.GetTicker()
Log(ticker.Last)
}
catch(err){
Log('GetTicker error: ', err)
Log(GetLastError()) //literal means, get last error
}
O FMZ envolve todos os dados de trocas diferentes no mesmo formato, o que facilita a escrita de uma estratégia multiplataforma. No entanto, você não pode obter os dados específicos de uma determinada API que fornecem informações adicionais e não pode acessar a API que o FMZ não suporta.
GetRawJSON
Retorna o conteúdo original (string) que foi devolvido pela última solicitação REST API, que pode ser usada para analisar as informações brutas por si mesmo.
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
Encontre todos os pormenores sobreHttpQuery
emhttps://fmz-docs.readthedocs.io/en/latest/code_Instruction/Global Function.html#httpquery
HttpQuery
Retorna os dados brutos desta solicitação que devem ser analisados primeiro.
//FMZ doesn't have a standard function for exchangeInfo that return the trading-information about all symbols.
var exchangeInfo = JSON.parse(HttpQuery('https://api.binance.com/api/v1/exchangeInfo'))
Log(exchangeInfo) // FMZ doesn't have a standard function for this API
var ticker = JSON.parse(HttpQuery('https://api.binance.com/api/v1/ticker/24hr'))
Log(ticker)
Para essas APIs públicas,HttpQuery
é realmente útil função.HttpQuery
Só suporta JavaScript, para Python, usando ourlib2
ourequest
biblioteca para enviar solicitações http diretamente.
O
Para essas APIs privadas, usandoHttpQuery
será muito complicado porque você precisa lidar com API-chave, sinal, hash, etc.IO
é uma função útil para esta condição, verifique-ohttps://fmz-docs.readthedocs.io/en/latest/code_Instruction/Extent API.html#io. IO
Nesta parte, nós apenas nos concentramos no acesso a APIs privadas.
Usar esta função requer a compreensão da API original da exchange primeiro. Abaixo estão os passos para fazer uma ordem de parada que não é suportada pelo FMZ no BitMEX.
POST
, Os parâmetros incluem símbolo, lado, ordemQty, stopPx,ordType, que deve ser organizado como "simbolo=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=StopO código JavaScript final:
var id = exchange.IO("api", "POST", "/api/v1/order", "symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=Stop")
Basicamente, todas as exchanges de moeda digital suportam o envio de dados de mercado via websocket, e algumas exchanges até suportam a atualização de informações de conta. Em comparação com o rest API, o websocket geralmente tem as vantagens de baixa latência, alta frequência e não é limitado pela frequência de solicitação do rest API da plataforma.
Para JavaScript, você pode usarDial
função para se conectar ao websocket, Para Python, você pode usarDial
ouwebsocket_client
libray.
Este tutorial irá focar na conexão de websockets usando o JavaScript eDial
Para estender os vários usos, a função Dial foi atualizada várias vezes. Este tutorial demonstrará a estratégia baseada em eventos baseada em websocket e como se conectar a várias trocas.
Conectado ao websocket
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
compress
significa que os dados estão em formato comprimido, e o parâmetromode
representa se o envio ou recebimento é comprimido.var client = Dial("wss://real.okex.com:10441/websocket?compress=true|compress=gzip_raw&mode=recv")
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr?reconnect=true")
var client = Dial("wss://ws-feed.pro.coinbase.com", 60)
client.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')
Receber dados
Geralmente, os dados do websocket podem ser lidos continuamente sem dormir em um loop infinito.
function main() {
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
while (true) {
var msg = client.read() //receve data from client
var data = JSON.parse(msg) //change raw string to js object
// do something, don't need sleep.
}
}
Websocket empurra dados muito rapidamente. O subjacente do docker cache todos os dados na fila, e depois retorna o primeiro quando o programa chamaread
. As operações de rede do robô, tais comoBuy
,GetAccount
,CancelOrder
Para informações como transação push, push de conta, subconjunto deep push, etc, precisamos de dados históricos. Para dados de mercado, geralmente só nos preocupamos com o mais recente.
Oread()
função retorna os dados mais antigos na fila se não houver argumentos, e bloqueia quando não há dados (o programa é interrompido aqui).read(-2)
para devolver imediatamente os dados mais recentes, e devolvernull
se não houver dados na fila (o programa não vai pausar).
Conectar-se a vários websockets
Neste caso, é óbvio que o programa não pode usar simplesread()
O tratamento geral é o seguinte:
function main() {
var binance = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
var coinbase = Dial("wss://ws-feed.pro.coinbase.com")
coinbase.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')
while (true) {
var msgBinance = binance.read(-1)
var msgCoinbase = coinbase.read(-1)
if(msgBinance){
// Binance has new data
}
if(msgCoinbase){
// coinbase has new data
}
Sleep(1) // just sleep 1ms
}
}
Quadro geral para utilização de websocket
Uma vez que os dados push já foram usados, o programa é naturalmente escrito como um tipo orientado por eventos, prestando atenção à frequência de solicitação da API.
var tradeTime = Date.now()
var accountTime = Date.now()
function trade(data){
if(Date.now() - tradeTime > 2000){//only trade once within 2s
tradeTime = Date.now()
//trading code
}
}
function GetAccount(){
if(Date.now() - accountTime > 5000){//only get account once within 5s
accountTime = Date.now()
return exchange.GetAccount()
}
}
function main() {
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr|reconnect=true")
while (true) {
var msg = client.read()
var data = JSON.parse(msg)
var account = GetAccount()
trade(data)
}
}
Todos os prametros
Os parâmetros deDial(Address, Timeout)
:
Timeout
: tempo de interrupção da ligação
O endereço pode ser seguido por outros parâmetros que estão ligados com&
. Endereço e parâmetros são separados por|
,
Parâmetro | Descrição |
---|---|
compressão | Método de compressãogzip_raw , gzip . OKEX utilizagzip_raw |
Modo | pode serdual significa que tanto o envio como o recebimento precisam ser comprimidos,send significa enviar necessidade de ser comprimido erecv significa receber. |
Proxy | Configurações de proxy para ss5.socks5://name:pwd@192.168.0.1:1080 |
Conectar de novo | Reconnect=true para permitir a reconexão |
intervalo | interval é o intervalo de tentativa de reinicialização, por defeito é 1000 ms |
carga útil | A mensagem de assinatura que precisa ser enviada quando o wss se reconecta |
Os parâmetros deread()
- Não.
Quando o websocket foi desconectado,read()
devolverá string vazio.
Parâmetro | Nenhum | -1 | -2 | 2000 |
---|---|---|---|---|
fila não está vazia | devolver os dados mais antigos imediatamente | devolver os dados mais antigos imediatamente | devolver os últimos dados imediatamente | devolver os dados mais antigos imediatamente |
A fila está vazia. | Bloquear até que novos dados voltem | retornonull imediatamente |
retornonull imediatamente |
esperar menos de 2000 ms até que novos dados retornem, caso contrário, retornarnull |
A utilização declose()
- Não.
Feche a conexão do websocket.
function main() {
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr|reconnect=true")
client.close()
}
Você pode ter notado que todos os códigos que temos agora são single thread, execução sequencial.GO
A utilização deGo
quando você realmente se importa com o atraso e o consumo de tempo de cada solicitação API.
Método, Args)
Método: nome da função. Args: os args do método.
Lista de funções suportadas:GetTicker
, GetDepth
, GetTrades
, GetRecords
, GetAccount
, GetOrders
, GetOrder
, CancelOrder
, Buy
, Sell
, GetPosition
.
Um exemplo de JavaScript:
function main(){
var a = exchange.Go("GetTicker"); //GetTicker Asynchronous multithreaded execution
var b = exchange.Go("GetDepth");
var c = exchange.Go("Buy", 1000, 0.1);
var d = exchange.Go("GetRecords", PERIOD_H1);
// The above four operations are concurrent multi-threaded asynchronous execution, will not block and immediately return
var ticker = a.wait(); // Call wait method wait for return to asynchronous get ticker result
var depth = b.wait(); // Return depth, it is also possible to return null if it fails
var orderId = c.wait(1000); // Return the order number, 1 second timeout, timeout returns undefined, this object can continue to call wait until the last wait timeout
var records = d.wait(); // Wait for K-line result
var ret = d.wait(); // Here waits for an asynchronous operation that has waited and ended, returns null, and logs an error message.
}
wait()
função deve ser chamada apósGo
função, caso contrário, o recurso do thread irá acumular até 2000 e retornar um erro.
LogStatus
e Tabelas
LogStatus irá registrar uma mensagem ou tabelas na barra de status dos bots, irá atualizar a cada vez.
//Normal uses of LogStatus
LogStatus(" This is a normal status prompt")
LogStatus(" This is a red font status prompt #ff0000")
LogStatus(" This is a multi-line status message\n I'm the second line")
O LogStatus pode registrar tabelas na sua página de robôs.`
caracteres para ambos os lados e tratá-lo como um formato de mensagem complexo (atualmente suportado tabela).
var table = {type: 'table', title: ' Account information support color #ff0000', cols: ['BTC', 'ETH', 'USDT'], rows: [ ['free', 1, 2000], ['frozen', 0, 3000]]}
LogStatus('`' + JSON.stringify(table)+'`')
//Another example, information can also appear in multiple lines:
LogStatus("First line message\n" + JSON.stringify(table)+"`\n third line message")
//Log multiple tables in a group, switching by TAB:
var table1 = {type: 'table', title: ' Account information 1', cols: ['BTC', 'ETH', 'USDT'], rows: [ ['free', 1, 2000], ['frozen', 0, 3000]]}
var table2 = {type: 'table', title: ' Account information 2', cols: ['BTC', 'ETH', 'USDT'], rows: [ ['free', 1, 2000], ['frozen', 0, 3000]]}
LogStatus('`' + JSON.stringify([table1, table2])+'`')
Gráfico
Desenhe figuras na página de gestão de robôs.
Suporte gráfico HighStocks e HighCharts, verifiquehttps://www.highcharts.com/demoehttps://www.highcharts.com/stock/demoPara mais exemplos.
O objeto Chart tem um__isStock
Atributo que não existe no original.__isStock
é falso, o gráfico será exibido como HighCharts.__isStock
se for verdade, o gráfico será exibido como HighStocks.reset()
para limpar os dados do gráfico.
Um exemplo de JavaScript de usar o gráfico para desenhar os preços de dois símbolos:
// This chart is an object in the JS language. Before using the Chart function, we need to declare an object variable chart that configures the chart.
var chart = {
// Whether the mark is a general chart, if you are interested, you can change it to false and run it.
__isStock: true,
tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'}, // Zoom tool
title : { text : 'Spread Analysis Chart'}, // title
rangeSelector: { // Selection range
buttons: [{type: 'hour',count: 1, text: '1h'}, {type: 'hour',count: 3, text: '3h'}, {type: 'hour', count: 8, text: '8h'}, {type: 'all',text: 'All'}],
selected: 0,
inputEnabled: false
},
xAxis: { type: 'datetime'}, // The horizontal axis of the coordinate axis is the x axis and the current setting type is :time
yAxis : { // The vertical axis of the axis is the y axis, and the default value is adjusted with the data size.
title: {text: 'Spread'}, // title
opposite: false, // Whether to enable the right vertical axis
},
series : [ // Data series, this attribute is saved for each data series (line, K-line graph, label, etc...)
{name : "line1", id : "Line 1,buy1Price", data : []}, // The index is 0, the data array is stored in the index series of data
{name : "line2", id : "Line 2,lastPrice", dashStyle : 'shortdash', data : []},
// The index is 1, dashStyle is set: 'shortdash' ie: Set the dotted line.
]
};
function main(){
var ObjChart = Chart(chart); // Call the Chart function to initialize the chart.
ObjChart.reset(); // Empty the chart
while(true){
var nowTime = new Date().getTime(); // Get the timestamp of this poll, which is a millisecond timestamp. Used to determine the position of the X axis written to the chart.
var tickerOne = _C(exchanges[0].GetTicker); // Get market data
var tickerTwo = _C(exchanges[1].GetTicker);
ObjChart.add([0, [nowTime, tickerOne.Last]]); // Use the timestamp as the X value and buy the price as the Y value to pass the index 0 data sequence.
ObjChart.add([1, [nowTime, tickerTwo.Last]]); // Same as above
ObjChart.update(chart); // Update the chart to show it.
Sleep(2000);
}
}
Suporta a exibição de figuras múltiplas, um exemplo completo:https://www.fmz.com/strategy/136056
Template é uma biblioteca que encapsula muitos recursos avançados, o que torna mais fácil escrever sua estratégia. Para usar um modelo, você deve copiar o modelo que você precisa primeiro.https://www.fmz.com/strategy/27293e salvar. Em seguida, selecione-o na página de edição estratégia.As funções são chamadas após$.
no modelo JavaScript e depoisext.
no modelo Python.
function main() {
var isFirst = true
while (true) {
var records = exchange.GetRecords();
if (records && records.length > 0) {
$.PlotRecords(records, 'BTC')
if (isFirst) {
$.PlotFlag(records[records.length - 1].Time, 'Start', 'S')
isFirst = false
$.PlotHLine(records[records.length - 1].Close, 'Close')
}
}
var ticker = exchange.GetTicker()
if (ticker) {
$.PlotLine('Last', ticker.Last)
$.PlotTitle('Last ' + ticker.Last)
}
Sleep(60000)
}
}
Aqui está outro exemplo simples que usa o modelo de gráfico:https://www.fmz.com/strategy/121917
q25459768- Obrigado.
Ervas daninhasEstou a trabalhar neste tutorial. Levará alguns dias para ser concluído.