Tutorial avançado para a plataforma FMZ Quant

Autora:Ninabadass, Criado: 2022-03-22 09:00:57, Atualizado: 2022-03-29 10:02:52

[TOC] Antes de aprender este tutorial, você precisa estudarComece com a plataforma FMZ QuanteTutorial básico para a plataforma FMZ Quant, e tornar-se proficiente em linguagens de programação.O tutorial elementar abrange as funções mais usadas, mas há muitas funções e recursos que não foram introduzidos, e eles não serão abordados neste tutorial.Depois de aprender este tutorial, você será capaz de escrever mais estratégias gratuitas e personalizadas, e a plataforma FMZ Quant é apenas uma ferramenta.

Acesso aos dados brutos da plataforma

A plataforma FMZ Quant encapsula todas as plataformas suportadas. A fim de manter a uniformidade, nosso suporte para uma única API de plataforma ainda não está completo. Por exemplo, o GetRecords pode passar o número de linhas K ou o horário de início, enquanto é fixado na plataforma FMZ; algumas plataformas suportam encomenda por lotes, enquanto a FMZ não suporta isso, e assim por diante.Para interfaces públicas (como cotações de mercado), pode utilizarHttpQuery, e para interfaces criptografadas (que envolvam informações da conta), é necessário utilizarIO.Para parâmetros de entrada específicos, consulte o documento API da plataforma correspondente.Infocampo retorna informações brutas, mas ainda não faz diferença sobre o problema de não suportar interfaces.

GetRawJSON (em inglês)

Ele retorna o conteúdo bruto (correntes) solicitado pela última API REST, que pode ser usado para analisar as informações estendidas por si só.

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() Acesso a Interfaces Públicas

Para acessar interfaces públicas, Js pode usarHttpQuery, e Python pode usar pacotes relacionados, tais comourllibourequests.

HttpQuery é padrão para o método GET, e suporta mais funções; consulte o documento API para mais detalhes.

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"))

Exemplo de Python usando solicitações:

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()

IO Acesso a Interfaces Criptografadas

Para interfaces que exigem assinaturas API-KEY, a função IO pode ser usada, e os usuários só precisam se preocupar com os parâmetros de entrada, e o processo de assinatura específico será concluído pela camada subjacente.

A plataforma FMZ atualmente não suporta ordens de stop-loss do BitMEX, que podem ser implementadas através do IO, de acordo com as seguintes etapas:

  • primeiro, encontrar a página de instruções do BitMEX API:https://www.bitmex.com/api/explorer/;
  • Em seguida, encontrar o endereço de encomenda da BitMEX em:https://www.bitmex.com/api/v1/order, com o método dePOST; para FMZ já especificou internamente o endereço de base, você só precisa passar em /api/v1/order.
  • Parâmetros correspondentes:symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=Stop.

Código específico:

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"}))

Mais exemplos de IO:https://www.fmz.com/bbs-topic/3683

Usando websocket

Basicamente, todas as plataformas de criptomoedas suportam websocket para enviar cotações de mercado, e algumas plataformas suportam websocket para atualizar informações de conta. Comparado com o rest API, o websocket geralmente tem as vantagens, como baixa latência, alta frequência e não ser limitado pela frequência do rest API da plataforma, etc. A desvantagem é que há um problema de interrupção, cujo processamento não é intuitivo.

Este artigo irá principalmente introduzir como usar a linguagem JavaScript e como usar a função Dial encapsulada pela plataforma para se conectar, na plataforma FMZ Quant; para instruções específicas e parâmetros estão no documento, você pode procurar por Dial; para realizar várias funções, a função Dial foi atualizada várias vezes.

1. Conexão Websocket

Geralmente, conecte-se diretamente pelo Websocket; por exemplo, para obter Binance tricker push:

var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")

Se os dados retornados estiverem em formato comprimido, deve ser feita uma especificação ao ligar; compress refere-se ao formato comprimido e mode representa os dados retornados que precisam ser comprimidos; por exemplo, ao ligar com 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, que é feita pelo Golang subjacente. Se a conexão detectada falhar, ela será reconectada. Para os dados de solicitação já na url, como o exemplo do Binance agora, é muito conveniente e recomendado. Para aqueles que precisam enviar mensagens de assinatura, eles podem manter o mecanismo de reconexão sozinhos.

var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr|reconnect=true")

Para se inscrever em mensagens wss, algumas solicitações da plataforma estão no url, e algumas precisam enviar os próprios canais assinados, como o coinbase:

client = Dial("wss://ws-feed.pro.coinbase.com", 60)
client.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')

2.Conexão de interface criptografada

Geralmente, o websocket é usado para ler cotações de mercado, mas também pode ser usado para obter ordens e push de conta. O push de tais dados criptografados às vezes tem um longo atraso e deve ser usado com cautela. Como o método de criptografia é mais complicado, aqui estão alguns exemplos dados para referência. Observe que apenas o AccessKey é necessário, que pode ser definido como um parâmetro de estratégia. Se o SecretKey for necessário, ele pode ser chamado implicitamente pela função exchange.HMAC(() para garantir a segurança.

    //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)
        }
    }

3. Websocket de leitura

Geralmente, pode ser lido continuamente em um loop infinito.

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 
    }
}

A velocidade de transferência de dados do wss é muito rápida. A camada inferior do Golang armazenará em cache todos os dados na fila e, quando o programa chamar, os dados serão devolvidos. No entanto, operações como colocar uma ordem no bot causarão atrasos, o que pode resultar no acúmulo de dados. Para informações como transferência de execução de negociação, transferência de conta e transferência de interpolação de profundidade, precisamos dos dados do histórico. Para os dados do mercado de cotações, na maioria dos casos, nos preocupamos apenas com os dados mais recentes, não com os dados do histórico.

Seread()adiciona nenhum parâmetro, ele vai retornar os dados mais antigos, e bloquear até retornar quando não há dados.client.read(-2)para devolver os dados mais recentes imediatamente, mas quando não existem dados, ele devolverá nulo, que precisa ser julgado antes da referência.

Dependendo de como lidar com os dados cacheados antigos e se ele é bloqueado quando não há dados, read tem parâmetros diferentes, como mostrado na tabela abaixo, que parece complicado, mas torna o programa mais flexível.img

4.Conectando-se com múltiplas plataformas por Websocket

Neste caso, é óbvio que simplesmente usar read() não funciona no programa, porque uma plataforma bloqueará mensagens em espera e outra plataforma não receberá mesmo se houver novas mensagens.

    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
        }
    }

5.Problemas de desconexão e de reconexão

Esta parte do processamento é mais problemática, porque os dados de push podem ser interrompidos, ou o atraso de push é extremamente longo. Mesmo que o batimento cardíaco possa ser recebido, isso não significa que os dados ainda estejam sendo empurrados. Você pode definir um intervalo de evento; se nenhuma atualização for recebida após o intervalo, reconecte; é melhor comparar os resultados retornados por rest após um período de tempo, para ver se os dados são precisos. Para os casos especiais do Binance, você pode definir diretamente a reconexão automática.

6.Utilizando o Framework de Programa Geral do Websocket

Para os dados de push foram usados, o programa será naturalmente escrito como evento desencadeado; prestar atenção à frequência de dados de push, porque os pedidos de alta frequência levarão a ser bloqueado; geralmente você pode escrever:

    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)
        }
    }

7.Conclusion

O método de conexão, o método de transmissão de dados, o conteúdo assinado e o formato de dados do websocket em cada plataforma são muitas vezes diferentes, então a plataforma não o encapsula e precisa usar a função Dial para se conectar sozinha.

PS: Embora algumas plataformas não forneçam citações de websocket, na verdade, quando você entra no site para usar a função de depuração, você descobrirá que todos eles estão usando o websocket push.

Concurrência de múltiplos fios

O JavaScript pode realizar a concorrência pela função Go, e o Python pode usar a biblioteca de multithreads correspondente.

Durante a realização de estratégias quantitativas, a execução simultânea pode reduzir o atraso de tempo e melhorar a eficiência.

var depthA = exchanges[0].GetDepth()
var depthB = exchanges[1].GetDepth()

Quando uma solicitação de API de repouso é atrasada, por exemplo, o tempo de atraso é de 100 milissegundos, então o tempo para obter a profundidade duas vezes é realmente diferente; se mais acessos forem necessários, os problemas de atraso serão mais óbvios, o que afetará a execução da estratégia.

Como o JavaScript não tem multithread, a camada subjacente encapsula a função Go para resolver este problema.GetDepth, GetAccounte assim por diante.IOé também apoiada, como:exchange.Go("IO", "api", "POST", "/api/v1/contract_batchorder", "orders_data=" + JSON.stringify(orders)), mas devido ao mecanismo de concepção, é mais tedioso de implementar.

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()

Em casos mais simples, escrever estratégias dessa maneira é bom. Mas note que o processo é repetido toda vez que o loop da estratégia, e as variáveis intermediárias a e b são na verdade apenas temporariamente auxiliares. Se temos muitas tarefas simultâneas, precisamos registrar adicionalmente a correspondência entre a e depthA, b e depthB. Quando nossas tarefas simultâneas são incertas, a situação é mais complicada. Portanto, esperamos realizar uma função: ao escrever a função Go simultaneamente, vincular uma variável ao mesmo tempo; quando o resultado de execução simultânea retorna, o valor do resultado é automaticamente atribuído à variável, eliminando assim a necessidade de fazer com que as variáveis intermediárias e o programa sejam mais concisos. A implementação específica é a seguinte:

function G(t, ctx, f) {
    return {run:function(){
        f(t.wait(1000), ctx)
    }}
}

Nós definimos uma função G, onde o parâmetro t é a função Go a ser executada, ctx é a função de registro do contexto do programa, e f é a função que atribui um valor específico.

Neste momento, a estrutura geral do programa pode ser escrita como um modelo, semelhante ao modelo produtor-consumidor (com algumas diferenças), o produtor envia continuamente tarefas e o consumidor as executa simultaneamente.

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)
    }
}

Parece que apenas uma função simples foi implementada nas operações acima. Na verdade, isso simplificou muito a complexidade do código. Nós só precisamos nos preocupar com quais tarefas o programa precisa gerar, e o programa worker() irá automaticamente executá-los simultaneamente e retornar os resultados correspondentes. A flexibilidade melhorou muito.

Desenho por função de gráfico

No tutorial elementar, a biblioteca de classes de desenho é recomendada na introdução do desenho, que pode atender às necessidades na maioria dos casos.

Os parâmetros internos deChart({…})são os objetos de HighStock e HighCharts, mas um parâmetro adicional__isStockO FMZ suporta basicamente os módulos básicos de HighCharts e HighStock, mas não suporta módulos adicionais.

O exemplo específico de HighCharts:https://www.highcharts.com/demoExemplo de HighStock:https://www.highcharts.com/stock/demoVocê pode consultar os códigos nesses exemplos, e transplantar-los para FMZ convenientemente.

Você pode chamar adicionar ([indice de série ((como 0), dados]) para adicionar dados à série com o índice especificado. Chamar reset() para limpar os dados do gráfico; reset pode tomar um parâmetro de número e especificar a quantidade a ser salva. A exibição de gráficos múltiplos é suportada, que só precisa passar nos parâmetros da matriz durante a configuração, como: var chart = Chart([{...}, {...}, {...}]). Por exemplo, se Chart1 tem duas séries, Chart2 tem uma série, e Chart3 tem uma série, ao chamar adicionar, os IDs das séries 0 e 1 são especificados para representar separadamente os dados das duas séries no gráfico atualizado1; a série 2 é especificada para representar os dados da primeira série no gráfico ID2; a série 3 é especificada para representar o primeiro dos dados da série no gráfico 3.

Um exemplo específico:

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);
    }
}

Exemplo de utilização do layout do gráfico:https://www.fmz.com/strategy/136056

Teste de retrocesso avançado

Python Local Backtest

Endereço específico de código aberto:https://github.com/fmzquant/backtest_python

Instalação

Introduza o seguinte comando na linha de comando:

pip install https://github.com/fmzquant/backtest_python/archive/master.zip

Um exemplo simples

Configure os parâmetros do backtest no início do código de estratégia sob a forma de observação, e os detalhes serão exibidos no botão Salvar Configurações na página Editar Estratégia do site da FMZ.

'''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

Teste de retrocesso

Para estratégias completas precisam de loops infinitos, o erro EOF será levantado após o backtest terminar; portanto, devemos fazer a tolerância a falhas bem.

# !/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()         

Dados personalizados de backtest

exchange.SetData(arr) muda a fonte de dados de backtest e usa dados de linha K personalizados. O parâmetro arr é uma matriz, cujos elementos são dados de barra de linha K (ou seja: matriz de dados de linha K, que temporariamente só suporta backtest JavaScript).

no conjunto arr, o formato de dados de um único elemento:

[
    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 
]

A fonte de dados pode ser importada no Template.

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")
}

Observação: certifique-se de importar os dados personalizados primeiro (ou seja, chamar a função exchange.SetData para definir os dados) durante a inicialização. O período de dados de linha K personalizado deve ser consistente com o período de linha K da camada subjacente definido na página do backtest, ou seja: dados de linha K personalizados ; o tempo de uma linha K é de 1 minuto, portanto, o período da linha K da camada subjacente definida no backtest também deve ser definido em 1 minuto.

Utilização de plataformas não suportadas pela FMZ

Se a API de uma plataforma não suportada é exatamente a mesma que a de uma plataforma suportada, exceto o endereço de base, a plataforma não suportada pode ser suportada mudando o endereço de base. Para ser específico, selecione uma plataforma suportada ao adicionar uma plataforma, mas preencha a API-KEY da plataforma não suportada e use IO para mudar o endereço de base na estratégia, como:

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

Nem todas as plataformas são suportadas pela FMZ, mas nossa plataforma forneceu o método de acesso do protocolo geral.

  • Quando escrevemos o código para aceder a uma plataforma, o programa cria um serviço web.
  • Quando adicionar uma plataforma ao FMZ, especifique o endereço e a porta do serviço web.
  • Quando o docker está executando o bot da plataforma do protocolo geral, a solicitação de acesso à API na estratégia será enviada para o protocolo geral.
  • O protocolo geral acessará a plataforma por solicitação e retornará o resultado para o docker.

Em termos simples, o protocolo geral é como um intermediário, proxyando a solicitação do docker e devolvendo os dados, de acordo com o padrão correspondente. O código do protocolo geral precisa ser preenchido por si mesmo. Escrever o protocolo geral realmente significa que você pode acessar a plataforma exclusivamente e concluir a estratégia. FMZ oficial às vezes libera a versão exe do protocolo geral das plataformas. O protocolo geral também pode ser feito em Python, que pode então ser executado no docker como um bot comum.

Introdução específica do protocolo geral:https://www.fmz.com/bbs-topic/9120Exemplo de escrita de protocolo geral em Python:https://www.fmz.com/strategy/101399

Criar a sua própria plataforma quantitativa

Assim como várias operações de uma plataforma podem ser implementadas através da API, o site da FMZ também é baseado na API. Você pode solicitar sua própria API-KEY do site da FMZ para realizar funções, como create, restart, DeleteRobot, GetRobotList GetRobotLogs, etc. Consulte a seção API Extension of FMZ Platform no documento da API para mais detalhes.

Devido à poderosa extensão da plataforma FMZ Quant, você pode criar sua própria plataforma quantitativa baseada na extensão API, permitindo que seus usuários executem bots em sua plataforma, etc. Referência específica:https://www.fmz.com/bbs-topic/1697 .

Tornar-se sócio da FMZ

Promoção do NetEase Classroom

O mercado de negociação de criptomoedas tem atraído cada vez mais a atenção dos traders quantitativos devido à sua particularidade. Na verdade, a negociação de programas tornou-se a corrente principal da criptomoeda, e estratégias como hedging e market making estão sempre ativas no mercado.www.fmz.com) atualmente a maior comunidade e plataforma quantitativa de criptomoedas, tem ajudado milhares de iniciantes no caminho para a negociação quantitativa por mais de 4 anos.

Promoção daCurso de negociação quantitativa de criptomoedas no NetEase Cloud Classroom. Entre no NetEase Cloud Classroom e compartilhe seu link do curso (o link tem um curso único). Outros, que se registrarem e comprarem o curso através deste link, trarão 50% do total como comissão, ou seja, 10 yuans. Siga a conta pública do WeChat da NetEase Cloud Classroom Premium Course Promotion para retirar o dinheiro. Você também pode convidar outros a promover o curso no Weibo ou no grupo QQ.

Afiliados

Os consumidores que clicar no link da promoção, se registrar e recarregar dentro de meio ano, desfrutarão da política de que nossa empresa reembolsará de acordo com o valor efetivo na ordem válida. A comissão será devolvida à conta do promotor na forma de pontos. Os usuários podem trocar os pontos para o saldo da conta da plataforma FMZ em uma proporção de 10: 1, e os usuários também podem usar os pontos para trocar os produtos relacionados da FMZ Quant no futuro.https://www.fmz.com/bbs-topic/3828

Plataforma quântica FMZ para empresas

O site completo da FMZ pode ser implantado no servidor exclusivo de uma empresa ou equipe para controle completo e personalização funcional. O site da FMZ foi usado e testado por cerca de 100.000 usuários e alcançou alta disponibilidade e segurança, o que pode economizar tempo para equipes quantitativas e empresas. A versão empresarial é para equipes de negociação quantitativa de médio porte, provedores de serviços de futuros de commodities, etc. Entre em contato com o administrador para citações específicas.

Sistema de criação de mercado

O sistema profissional, que fornece a liquidez do mercado e a gestão de fundos para as plataformas, pode ser o sistema de criação de mercado mais melhorado do mercado.

Plano de plataforma

O sistema de negociação de tecnologia FMZ adota tecnologia de correspondência de memória, e a velocidade de processamento de pedidos é tão alta quanto 2 milhões de transações por segundo, o que pode garantir que não haverá atraso ou atraso no processamento de pedidos. Pode manter a operação suave e estável de plataformas com mais de 20 milhões de usuários online simultâneos. A estrutura do sistema de múltiplas camadas e multiclusters garante a segurança, estabilidade e extensão do sistema. A implantação de funções e atualizações de versões podem ser realizadas sem tempo de inatividade, o que garante o máximo da experiência operacional dos usuários do terminal.


Mais.