FMZ Tutorial intermediário

Autora:Ervas daninhas, Criado: 2019-04-12 14:28:19, Atualizado: 2024-02-05 20:07:56

[TOC]

img

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.

1.Adicionar várias bolsas e trocar vários símbolos

Você pode negociar em várias bolsas e vários símbolos dentro de um robô facilmente.

  • Adicione uma ou várias trocas ao iniciar o robô.
  • A API pode ser chamada comoexchange.GetTicker()quando uma troca é adicionada
  • Quando múltiplas trocas são adicionadas, a API é chamada comoexchanges[0].GetTicker(), exchanges[1].GetTicker() img
  • Pode adicionar a mesma troca com símbolos diferentes.img
  • Você pode alterar o símbolo que está ligado comexchangeatravés da utilizaçãoIOfunção
var 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)
  }
}

2.TradeContratos futuros e swaps

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.img

A imagem abaixo mostra como definir o símbolo de futuros para BTC ao iniciar o bot.img

Abaixo está como definir um tipo de contrato para cada troca.

  • OKEX
    exchange.SetContractType("swap")        
    exchange.SetContractType("this_week")   
    exchange.SetContractType("next_week")  
    exchange.SetContractType("quarter")   
  • HuobiDM
    exchange.SetContractType("this_week")   
    exchange.SetContractType("next_week")  
    exchange.SetContractType("quarter")     
  • BitMEX
    exchange.SetContractType("XBTUSD")  
    exchange.SetContractType("XBTM19") 
  • Portão
    exchange.SetContractType("swap")    
  • Deribit
    exchange.SetContractType("BTC-PERPETUAL")  
    exchange.SetContractType("BTC-27APR18")

3.Sobre o backtest

Introdução básica

O FMZ tem dois modos de backtesting:real tickesimulate 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:imgPedaços escondidos:img

Resultado do teste de regresso

img

4.Tolerância a erros

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.nullEntã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:imgVocê 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
 } 

5.Conectar-se directamente à bolsa

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 sobreHttpQueryemhttps://fmz-docs.readthedocs.io/en/latest/code_Instruction/Global Function.html#httpquery

HttpQueryRetorna 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.HttpQuerySó suporta JavaScript, para Python, usando ourlib2ourequestbiblioteca para enviar solicitações http diretamente.

O

Para essas APIs privadas, usandoHttpQueryserá 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. IONesta 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.

O código JavaScript final:

var id = exchange.IO("api", "POST", "/api/v1/order", "symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=Stop")

6.Utilize o websocket

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 usarDialfunção para se conectar ao websocket, Para Python, você pode usarDialouwebsocket_client libray.

Este tutorial irá focar na conexão de websockets usando o JavaScript eDialPara 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

  • 1.Na maioria dos casos, você pode se conectar diretamente.
    var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
    
  • 2.Para os dados de retorno do formato comprimido, estes devem ser especificados no momento da ligação.compresssignifica que os dados estão em formato comprimido, e o parâmetromoderepresenta se o envio ou recebimento é comprimido.
    var client = Dial("wss://real.okex.com:10441/websocket?compress=true|compress=gzip_raw&mode=recv")
    
  • A função Dial suporta a reconexão automática, que é feita pela linguagem Go subjacente. Para o conteúdo dos dados da solicitação já está na url, como o exemplo de Biannce, é conveniente e recomendado. Para aqueles que precisam enviar uma mensagem de assinatura, você pode manter a reconexão por conta própria.
    var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr?reconnect=true")
    
  • 4.Algumas bolsas de câmbio canais de assinatura estão incluídos no URL, como o Binance, mas alguns exigem que os usuários enviem canais assinados, como o coinbase:
    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,CancelOrderPara 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 devolvernullse 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 serdualsignifica que tanto o envio como o recebimento precisam ser comprimidos,sendsignifica enviar necessidade de ser comprimido erecvsignifica receber.
Proxy Configurações de proxy para ss5.socks5://name:pwd@192.168.0.1:1080
Conectar de novo Reconnect=truepara 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 retornonullimediatamente retornonullimediatamente 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()
}
    

7.Asíncrono ou multicorrente

Você pode ter notado que todos os códigos que temos agora são single thread, execução sequencial.GOA utilização deGoquando 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ósGofunção, caso contrário, o recurso do thread irá acumular até 2000 e retornar um erro.

8.Tabelas e gráficos

LogStatuse 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__isStockAtributo que não existe no original.__isStocké falso, o gráfico será exibido como HighCharts.__isStockse 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 img

9.Modelo de estratégia

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.imgAs 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


Mais.

q25459768- Obrigado.

Ervas daninhasEstou a trabalhar neste tutorial. Levará alguns dias para ser concluído.