Tutorial intermedio de FMZ

El autor:Las hierbas, Creado: 2019-04-12 14:28:19, Actualizado: 2024-02-05 20:07:56

[TOC] ¿Qué quieres decir?

img

Este tutorial cubrirá más detalles sobre la plataforma FMZ, más habilidades prácticas sobre el uso de la API.

Después de aprender todo el tutorial, usted hará un uso pleno de FMZ y ser capaz de escribir estrategias más personalizadas, más eficientes y más complejas.

1.Agregar múltiples intercambios y intercambiar múltiples símbolos

Puede operar en múltiples intercambios y múltiples símbolos dentro de un robot fácilmente.

  • Añadir un intercambio o múltiples intercambios al iniciar el robot.
  • La API puede llamarse comoexchange.GetTicker()cuando se añade un intercambio
  • Cuando se agregan múltiples intercambios, la API se llama comoexchanges[0].GetTicker(), exchanges[1].GetTicker() img
  • Puedes añadir el mismo intercambio con diferentes símbolos.img
  • Puede cambiar el símbolo que está vinculado conexchangemediante el uso deIOFunción
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 de futuros y swaps

Hasta ahora, FMZ apoya todos los principales intercambios de futuros, como OKEX, HuobiDM, BitMEX, GateIO y Deribit, y sus contratos de swap.

Para negociar futuros en FMZ, primero debes agregar un intercambio de futuros, establecer el símbolo al iniciar el bot y establecer el tipo de contrato en tu código.

si un exchange admite tanto spot como futuros, deben añadirse a FMZ por separado.img

La imagen a continuación muestra cómo configurar el símbolo de futuros en BTC al iniciar el bot.img

A continuación se muestra cómo establecer un tipo de contrato para cada intercambio.

  • Está bien.
    exchange.SetContractType("swap")        
    exchange.SetContractType("this_week")   
    exchange.SetContractType("next_week")  
    exchange.SetContractType("quarter")   
  • - ¿ Qué es eso?
    exchange.SetContractType("this_week")   
    exchange.SetContractType("next_week")  
    exchange.SetContractType("quarter")     
  • BitMEX
    exchange.SetContractType("XBTUSD")  
    exchange.SetContractType("XBTM19") 
  • Puerta de entrada
    exchange.SetContractType("swap")    
  • El deribit
    exchange.SetContractType("BTC-PERPETUAL")  
    exchange.SetContractType("BTC-27APR18")

3.Acerca de las pruebas de retroceso

Introducción básica

FMZ tiene dos modos de backtesting:real tickysimulate tick. El nivel de tick real contiene todos los datos históricos completados (un tick por segundo), por lo que los resultados de backtesting son más confiables. El nivel de simulación utiliza los datos de los clínicos de la historia en el intervalo utilizado por su estrategia. Los ticks dentro de un kline son generados por un algoritmo que es el mismo que MT4, puede encontrar más detalles enhttps://www.mql5.com/en/articles/75Mientras tanto, un intervalo más corto puede ser elegido como base-clín para generar garrapatas. El modo de simulación de ticks es mucho más rápido pero menos preciso que el modo de simulación real.

Configuración de pruebas de retroceso

Aquí están las configuraciones predeterminadas:imgPuntos ocultos:img

Resultado de la prueba posterior

img

4.Tolerancia a los errores

Al llamar a cualquier función que acceda a la API de intercambio (comoGetTicker, Buy, CancelOrder, etc...), puede obtener un fallo de acceso debido a un problema del servidor de intercambio, parámetros incorrectos, problema de transmisión de red, etc. En este caso, la función devolveránullAsí que necesitas saber cómo lidiar con los errores.

¿Cuál es el error?

El bot devolverá un mensaje de error cuando ocurra un error. Solo buscando el nombre de intercambio + mensaje de error, puede encontrar cuál es el problema. Por ejemplo, Un error{"result":false,"error_code":20049}se devuelve cuando se llamaexchange.GetAccount()en OKEX.OKEX 20049, este es el resultado:imgTambién puede comprobar el código de error en el documento de intercambio API, tales comoCódigo de error de OKEX para futuros

Errores de trato

Debes considerar cómo manejar los errores al escribir el código de estrategia.

 // 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.Conectarse directamente al intercambio

FMZ envuelve todos los datos de los diferentes intercambios en el mismo formato, lo que facilita la escritura de una estrategia multiplataforma. Sin embargo, no puede obtener los datos específicos de una determinada API que proporcionan información adicional y no puede acceder a la API que FMZ no admite. Hay dos soluciones para este problema.

ObtenerRawJSON

Devuelva el contenido original (cuadrícula) que fue devuelto por la última solicitud de API REST, que puede usarse para analizar la información sin procesar usted mismo.

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 (cuestionario de búsqueda)

Encuentra todos los detalles sobreHttpQueryEn elEn el caso de las aplicaciones que no se utilicen en el sistema, el valor de las aplicaciones se calculará en función de las características de las aplicaciones.

HttpQuerydevuelve los datos en bruto de esta solicitud que debe ser analizado primero.

//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 esas APIs públicas,HttpQueryEs una función muy útil.HttpQuerysólo admite JavaScript, para Python, utilizando elurlib2o bienrequestbiblioteca para enviar solicitudes http directamente.

- ¿ Qué?

Para esas API privadas, usandoHttpQueryserá muy complicado porque usted necesita para tratar con API-clave, signo, hash, etc.IOes una función útil para esta condición, comprobar enSe trata de una aplicación de software para la gestión de datos.. IOEn esta parte, sólo nos centramos en el acceso a las API privadas.

El uso de esta función requiere primero la comprensión de la API original del exchange.

  • En primer lugar, encontrar la página de documentación de BitMEX API:https://www.bitmex.com/api/explorer/
  • La url para hacer una orden de parada eshttps://www.bitmex.com/api/v1/orderEl método esPOST, Los parámetros incluyen símbolo, lado, ordenQty, stopPx,ordType, que debe estar organizado como "símbolo=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=Stop

El código JavaScript final:

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

6.Utilizar el soporte web

Básicamente, todos los intercambios de divisas digitales admiten el envío de datos de mercado a través de websocket, y algunos intercambios incluso admiten la actualización de la información de la cuenta.

Para JavaScript, puedes usarDialFunción para conectar a websocket, Para Python, se puede utilizarDialo bienwebsocket_client libray.

Este tutorial se centrará en la conexión de websockets utilizando el JavaScript yDialLa función Dial ha sido actualizada varias veces para ampliar los diversos usos. Este tutorial demostrará la estrategia basada en eventos basados en websocket y cómo conectarse a múltiples intercambios.

Conectado al soporte web

  • 1.En la mayoría de los casos, puede conectarse directamente.A continuación se muestra un ejemplo de conexión a Binance all ticker.
    var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
    
  • 2.En el caso de los datos de retorno del formato comprimido, es necesario especificarlos en el momento de la conexión.compresssignifica que los datos están en formato comprimido, y el parámetromoderepresenta si el envío o la recepción se comprime.
    var client = Dial("wss://real.okex.com:10441/websocket?compress=true|compress=gzip_raw&mode=recv")
    
  • 3.La función Dial admite la reconexión automática, que se realiza mediante el lenguaje Go subyacente. Para el contenido de los datos de la solicitud ya está en la url, como el ejemplo de Biannce, es conveniente y recomendable. Para aquellos que necesitan enviar un mensaje de suscripción, puede mantener la reconexión por sí mismo.
    var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr?reconnect=true")
    
  • 4.Algunos intercambios canales de suscripción están incluidos en la url, como Binance, pero algunos requieren que los usuarios envíen canales suscritos, como coinbase:
    var client = Dial("wss://ws-feed.pro.coinbase.com", 60)
    client.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')
    

Recibir datos

Generalmente, los datos de websocket pueden leerse continuamente sin dormir en un bucle 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. 
    }
}

El subyacente del docker almacena todos los datos en la cola, y luego devuelve el primero cuando el programa llamareadLas operaciones de red del robot, tales comoBuy,GetAccount,CancelOrderPara la información como el impulso de la transacción, el impulso de la cuenta, el impulso profundo del subconjunto, etc., necesitamos datos históricos. Para los datos del mercado, generalmente solo nos preocupamos por lo último.

Elread()función devuelve los datos más antiguos en la cola si no hay argumentos, y bloquea cuando no hay datos (el programa se detiene aquí).read(-2)para devolver inmediatamente los datos más recientes, y devolvernullsi no hay datos en la cola ((el programa no se detendrá).

Conectarse a varios websockets

En este caso, es obvio que el programa no puede utilizar simpleread()El tratamiento general es el siguiente:

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

Marco general para el uso de websocket

Dado que los datos push ya se han utilizado, el programa se escribe naturalmente como un tipo impulsado por eventos, prestando atención a la frecuencia de solicitud de 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 los prámetros

Los parámetros deDial(Address, Timeout): Timeout: tiempo de interrupción de conexión La dirección puede ser seguida por otros parámetros que están conectados con&La dirección y los parámetros están separados por|,

Parámetro Descripción
comprimir Método de compresióngzip_raw, gzip. OKEX utilizagzip_raw
el modo puede serdualsignifica que tanto el envío como el recibimiento deben comprimirse,sendsignifica enviar necesidad de ser comprimido yrecvsignifica recibir.
Proxy (procesión) Configuración de proxy para ss5.socks5://name:pwd@192.168.0.1:1080
vuelve a conectar Reconnect=truepara permitir la reconexión
el intervalo intervales el intervalo de reintentos, por defecto es 1000 ms
Carga útil El mensaje de suscripción que debe ser enviado cuando wss se vuelve a conectar

Los parámetros deread()En el caso de: Cuando el websocket se desconectó,read()regresará una cadena vacía.

Parámetro No hay -1 -2 2000
la cola no está vacía devuelve los datos más antiguos inmediatamente devuelve los datos más antiguos inmediatamente devuelve los últimos datos inmediatamente devuelve los datos más antiguos inmediatamente
la cola está vacía Bloquear hasta que vuelvan los datos. regresonullinmediatamente. regresonullinmediatamente. esperar menos de 2000 ms hasta que vuelvan los nuevos datos, de lo contrario, regresarnull

El uso declose()- ¿ Por qué? Cierra la conexión del websocket.

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

7.Asíncrono o con múltiples hilos

Puede que hayas notado que todos los códigos que tenemos ahora son de hilo único, ejecución secuencial.GOEn la actualidad, la mayoría de los países de la Unión Europea tienen una gran capacidad para hacer eso, lo que es muy limitado.Gocuando realmente te preocupas por el retraso y el consumo de tiempo de cada solicitud de API.

el intercambio.Go (Método, Args)

Método: nombre de una función. Args: los argumentos del método.

Lista de funciones soportadas:GetTicker, GetDepth, GetTrades, GetRecords, GetAccount, GetOrders, GetOrder, CancelOrder, Buy, Sell, GetPosition.

Un ejemplo 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()La función debe ser llamada despuésGoFunción, de lo contrario, el recurso de hilo se acumulará hasta 2000 y devolverá un error.

8.Tablas y gráficos

LogStatusy Tablas

LogStatus registrará un mensaje o tablas en la barra de estado de los bots, se actualizará 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")

LogStatus puede registrar tablas en su página de robot.`caracteres a ambos lados y tratarlo como un formato de mensaje complejo (tabla actualmente soportada).

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

Dibuja figuras en la página de gestión de robots. Apoyo a los gráficos HighStocks y HighCharts, comprobarhttps://www.highcharts.com/demoyhttps://www.highcharts.com/stock/demopara más ejemplos. El objeto Diagrama tiene un__isStockatributo que no existe en el original.__isStockes falso, el gráfico se mostrará como HighCharts.__isStocksi es cierto, el gráfico se mostrará como HighStocks.reset()para borrar los datos del gráfico.

Un ejemplo de JavaScript de usar el gráfico para dibujar los precios de dos 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);
    }
}

Apoya la visualización de varias figuras, un ejemplo completo:https://www.fmz.com/strategy/136056 img

9.Plantilla de estrategia

La plantilla es una biblioteca que encapsula muchas características avanzadas, lo que facilita la escritura de su estrategia. Para usar una plantilla, primero debe copiar la plantilla que necesita.https://www.fmz.com/strategy/27293y guardar. Luego selecciona en la página de edición de estrategia.imgLas funciones se llaman después$.en la plantilla JavaScript y despuésext.en la plantilla de 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)
    }
}

Aquí hay otro ejemplo simple que utiliza plantilla de trama:https://www.fmz.com/strategy/121917


Más.

el nombre de la entidad- Gracias.

Las hierbasEstoy trabajando en este tutorial. Tomará unos días completarlo. Siéntase libre de hacer cualquier pregunta.