En la carga de los recursos... Cargando...

Tutoriales avanzados para escribir estrategias de la plataforma FMZ Quant

El autor:FMZ~Lydia, Creado: 2023-07-12 14:49:24, Actualizado: 2024-01-04 21:04:23

[TOC] ¿Qué quieres decir?

img

Antes de aprender este tutorial, usted necesita estudiarComience con la plataforma cuántica FMZyTutorial básico para la plataforma FMZ Quant para escribir estrategias, y llegar a ser experto en lenguajes de programación.El tutorial elemental cubre las funciones más utilizadas, pero hay muchas funciones y características que no se han introducido, y no serán cubiertas en este tutorial.Después de aprender este tutorial, usted será capaz de escribir más estrategias gratuitas y personalizadas, y la plataforma FMZ Quant es sólo una herramienta.

Acceso a los datos en bruto de la plataforma

La plataforma FMZ Quant encapsula todas las plataformas compatibles. Para mantener la uniformidad, nuestro soporte para una API de plataforma única todavía no está completo. Por ejemplo, GetRecords puede pasar el número de K-líneas o la hora de inicio, mientras que está fijado en la plataforma FMZ; algunas plataformas admiten el pedido por lotes, mientras que FMZ no lo admite, y así sucesivamente. Por lo tanto, hay una necesidad de una manera de acceder directamente a los datos de la plataforma.Para las interfaces públicas (como las cotizaciones de mercado), puede utilizarHttpQuery, y para las interfaces cifradas (que involucran información de la cuenta), debe utilizarIO.Para los parámetros entrantes específicos, consulte el documento correspondiente de la API de la plataforma.Infocampo devuelve información en bruto, pero todavía no hace ninguna diferencia en el problema de no soportar interfaces.

¿ Qué es eso?

Devuelve el contenido en bruto (cuadros) solicitado por la última API REST, que se puede utilizar para analizar la información extendida por sí misma.

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() Acceso a las interfaces públicas

Para acceder a las interfaces públicas, Js puede utilizarHttpQuery, y Python puede utilizar paquetes relacionados, tales comourllibo bienrequests.

HttpQuery es predeterminado para el método GET, y admite más funciones; consulte el documento de la API para más detalles.

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

Ejemplo de Python usando peticiones:

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 accede a las interfaces cifradas

Para las interfaces que requieren firmas API-KEY, se puede usar la función IO, y los usuarios solo necesitan preocuparse por los parámetros entrantes, y el proceso de firma específico será completado por la capa subyacente.

La plataforma FMZ actualmente no admite órdenes de stop-loss de BitMEX, que se pueden implementar a través de IO, de acuerdo con los siguientes pasos:

  • En primer lugar, encontrar la página de instrucciones de BitMEX API:https://www.bitmex.com/api/explorer/;
  • Luego, encuentre la dirección de pedido de BitMEX en:https://www.bitmex.com/api/v1/order, con el método dePOST; para FMZ ya ha especificado internamente la dirección de base, sólo tiene que pasar en /api/v1/orden.
  • los parámetros correspondientes: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"}))

Más ejemplos de IO:https://www.fmz.com/bbs-topic/3683

Utilizando el soporte web

Básicamente, todas las plataformas de criptomonedas admiten websocket para enviar cotizaciones de mercado, y algunas plataformas admiten websocket para actualizar la información de la cuenta.

Este artículo presentará principalmente cómo usar el lenguaje JavaScript y cómo usar la función Dial encapsulada por la plataforma para conectarse, en la plataforma FMZ Quant; para instrucciones y parámetros específicos están en el documento, puede buscar Dial; para realizar varias funciones, la función Dial ha sido actualizada varias veces.

1. Conexión Websocket

Generalmente, se conecta directamente por Websocket; por ejemplo, para obtener Binance tricker push:

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

Si los datos devueltos están en un formato comprimido, se debe especificar al conectarse; compress se refiere al formato comprimido y mode representa qué datos devueltos deben comprimirse; por ejemplo, al conectarse con OKEX:

var client = Dial("wss://real.okex.com:10441/websocket?compress=true|compress=gzip_raw&mode=recv")

La función Dial admite la reconexión, que es realizada por el Golang subyacente. Si la conexión detectada se rompe, se volverá a conectar. Para los datos de solicitud ya en la url, como el ejemplo de Binance ahora, es muy conveniente y recomendado. Para aquellos que necesitan enviar mensajes de suscripción, pueden mantener el mecanismo de reconexión ellos mismos.

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

Para suscribirse a los mensajes de wss, algunas solicitudes de la plataforma están en la url, y algunas necesitan enviar los canales suscritos ellos mismos, como coinbase:

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

2. Conexión de interfaz cifrada

Por lo general, el websocket se utiliza para leer cotizaciones de mercado, pero también se puede utilizar para obtener pedidos y empuje de cuenta. El empuje de dichos datos cifrados a veces tiene un largo retraso y debe usarse con precaución. Dado que el método de cifrado es más complicado, aquí hay algunos ejemplos dados para referencia. Tenga en cuenta que solo se requiere AccessKey, que se puede establecer como un parámetro de estrategia. Si SecretKey es necesaria, puede ser llamada implícitamente por la función exchange.HMAC(() para garantizar la seguridad.

    //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 lectura

En general, se puede leer continuamente en un bucle 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 
    }
}

La velocidad de empuje de datos wss es muy rápida. La capa inferior de Golang almacenará en caché todos los datos en la cola, y cuando el programa lea las llamadas, los datos serán devueltos a su vez. Sin embargo, operaciones como la colocación de un pedido en el bot causarán demoras, lo que puede resultar en la acumulación de datos. Para información como el empuje de ejecución de operaciones, el empuje de cuenta e interpolación de profundidad, necesitamos los datos del historial. Para los datos del mercado de cotizaciones, en la mayoría de los casos, solo nos preocupamos por los datos más recientes, no por los datos del historial.

Si esread()si no añade parámetros, devolverá los datos más antiguos y bloqueará hasta que no haya datos.client.read(-2)para devolver los datos más recientes inmediatamente, pero cuando no hay datos, devolverá nulo, que debe ser juzgado antes de la referencia.

Dependiendo de cómo manejar los datos almacenados en caché y si se bloquean cuando no hay datos, read tiene diferentes parámetros, como se muestra en la tabla a continuación, lo que parece complicado, pero hace que el programa sea más flexible.img

4.Conexión con múltiples plataformas mediante Websocket

En este caso, es obvio que simplemente usar read() no funciona en el programa, porque una plataforma bloqueará los mensajes en espera, y otra plataforma no recibirá incluso si hay nuevos mensajes.

    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 desconexión y reconexión

Esta parte del procesamiento es más problemática, ya que los datos de empuje pueden ser interrumpidos, o el retraso de empuje es extremadamente largo. Incluso si se puede recibir el latido del corazón, no significa que los datos todavía estén siendo empujados. Puede establecer un intervalo de eventos; si no se recibe ninguna actualización después del intervalo, vuelva a conectar; es mejor comparar los resultados devueltos por rest después de un período de tiempo, para ver si los datos son exactos. Para los casos especiales de Binance, puede establecer directamente la reconexión automática.

6.Utilizando el marco de programa general de Websocket

Para los datos de empuje se ha utilizado, el programa se escribirá naturalmente como evento desencadenado; prestar atención a la frecuencia de datos de empuje, porque las solicitudes de alta frecuencia conducirá a ser bloqueado; en general se puede escribir:

    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

El método de conexión, el método de transmisión de datos, el contenido suscrito y el formato de datos del websocket en cada plataforma a menudo son diferentes, por lo que la plataforma no lo encapsula y necesita usar la función Dial para conectarse por sí misma.

PS: Aunque algunas plataformas no proporcionan citas de websocket, de hecho, cuando inicia sesión en el sitio web para usar la función de depuración, encontrará que todos están usando el empuje de websocket.

Concurrencia de múltiples hilos

JavaScript puede realizar la concurrencia por función Go, y Python puede usar la biblioteca de multithread correspondiente.

Durante la realización de estrategias cuantitativas, la ejecución simultánea puede reducir el retraso de tiempo y mejorar la eficiencia.

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

Cuando una solicitud de API de descanso se retrasa, por ejemplo, el tiempo de retraso es de 100 milisegundos, entonces el tiempo para obtener la profundidad dos veces es realmente diferente; si se necesitan más accesos, los problemas de retraso serán más obvios, lo que afectará la ejecución de la estrategia.

Dado que JavaScript no tiene multithread, la capa subyacente encapsula la función Go para resolver este problema.GetDepth, GetAccounty así sucesivamente.IOTambién se apoya, por ejemplo:exchange.Go("IO", "api", "POST", "/api/v1/contract_batchorder", "orders_data=" + JSON.stringify(orders)), pero debido al mecanismo de diseño, es más 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()

En la mayoría de los casos simples, escribir estrategias de esta manera está bien. Pero tenga en cuenta que el proceso se repite cada vez que los bucles de la estrategia, y las variables intermedias a y b son en realidad sólo temporalmente auxiliares. Si tenemos muchas tareas concurrentes, necesitamos registrar adicionalmente la correspondencia entre a y profundidadA, b y profundidadB. Cuando nuestras tareas concurrentes son inciertas, la situación es más complicada. Por lo tanto, esperamos realizar una función: cuando se escribe la función Go simultáneamente, vincular una variable al mismo tiempo; cuando el resultado de ejecución concurrente devuelve, el valor del resultado se asigna automáticamente a la variable, eliminando así la necesidad de hacer variables intermedias y el programa más conciso. La implementación específica es la siguiente:

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

Hemos definido una función G, donde el parámetro t es la función Go a ejecutar, ctx es la función de registro del contexto del programa, y f es la función que asigna un valor específico.

En este momento, el marco general del programa se puede escribir como un modelo, similar al modelo producer-consumer (con algunas diferencias), el productor envía continuamente tareas y el consumidor las ejecuta simultáneamente.

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 sólo se ha implementado una función simple en las operaciones anteriores. De hecho, eso ha simplificado enormemente la complejidad del código. Solo necesitamos preocuparnos por las tareas que el programa necesita generar, y el programa worker() las ejecutará automáticamente simultáneamente y devolverá los resultados correspondientes. La flexibilidad ha mejorado mucho.

Dibujo por función de gráfico

En el tutorial elemental, se recomienda la biblioteca de clases de dibujo en la introducción del dibujo, que puede satisfacer las necesidades en la mayoría de los casos.

Los parámetros internos deChart({…})son los objetos de HighStock y HighCharts, pero un parámetro adicional__isStockEl FMZ admite básicamente los módulos básicos de HighCharts y HighStock, pero no admite módulos adicionales.

El ejemplo específico de HighCharts:https://www.highcharts.com/demoEjemplo de HighStock:https://www.highcharts.com/stock/demoPuedes consultar los códigos en esos ejemplos, y trasplantarlos a FMZ convenientemente.

Puede llamar add ([índice de serie ((como 0), datos]) para agregar datos a la serie con el índice especificado. Llama reset (() para aclarar los datos del gráfico; reset puede tomar un parámetro de número y especificar la cantidad a guardar. Se admite la visualización de múltiples gráficos, que solo necesita pasar en los parámetros de la matriz durante la configuración, como: var chart = Chart (([{...}, {...}, {...}). Por ejemplo, si Chart1 tiene dos series, Chart2 tiene una serie, y Chart3 tiene una serie, al llamar add, se especifican los ID de serie 0 y 1 para representar por separado los datos de las dos series en el gráfico actualizado1; la serie 2 se especifica para representar los datos de la primera serie en Chart2; la serie ID 3 se especifica para representar el primero de los datos de la serie en Chart3.

Un ejemplo concreto:

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

Ejemplo de uso del diseño del gráfico:https://www.fmz.com/strategy/136056

Prueba de retroceso avanzada

Prueba de retroceso local de Python

Dirección de código abierto específica:https://github.com/fmzquant/backtest_python

Instalación

Introduzca el siguiente comando en la línea de comandos:

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

Ejemplo sencillo

Establezca los parámetros de backtest al comienzo del código de estrategia en forma de observación, y los detalles se mostrarán en el botón de Guardar configuraciones en la página Edición de estrategia del sitio web de 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

Prueba de retroceso

Para las estrategias completas necesitan bucles infinitos, el error EOF se levantará después de que finalice la prueba de retroceso; por lo tanto, debemos hacer bien la tolerancia a fallos.

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

Datos personalizados de pruebas de retroceso

exchange.SetData(arr) cambia la fuente de datos de backtest y utiliza datos de línea K personalizados. El parámetro arr es una matriz, cuyos elementos son datos de barra de línea K (es decir: matriz de datos de línea K, que temporalmente solo admite backtest JavaScript).

en el conjunto de arr, el formato de datos de un solo 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 
]

La fuente de datos se puede importar en la plantilla .

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

Nota: asegúrese de importar primero los datos personalizados (es decir, llame a la función exchange.SetData para establecer los datos) durante la inicialización. El período de datos de línea K personalizado debe ser consistente con el período de línea K de la capa inferior establecido en la página de backtest, es decir: datos de línea K personalizados ; el tiempo de una línea K es de 1 minuto, por lo que el período de la línea K de la capa inferior establecida en la backtest también debe establecerse en 1 minuto.

Utilizando plataformas no soportadas por FMZ

Si la API de una plataforma no soportada es exactamente la misma que la de una plataforma soportada, excepto la dirección base, la plataforma no soportada puede ser soportada cambiando la dirección base. Para ser específico, seleccione una plataforma soportada al agregar una plataforma, pero rellene la API-KEY de la plataforma no soportada, y use IO para cambiar la dirección base en la estrategia, 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

No todas las plataformas son compatibles con FMZ, pero nuestra plataforma ha proporcionado el método de acceso del protocolo general.

  • Cuando escribe el código para acceder a una plataforma, el programa creará un servicio web.
  • Cuando añada una plataforma a FMZ, especifique la dirección y el puerto del servicio web.
  • Cuando el docker está ejecutando el bot de la plataforma del protocolo general, la solicitud de acceso a la API en la estrategia se enviará al protocolo general.
  • El protocolo general accederá a la plataforma por solicitud, y devuelve el resultado al docker.

En pocas palabras, el protocolo general es como un intermediario, proxying la solicitud del docker y devolver los datos, de acuerdo con el estándar correspondiente. El código del protocolo general necesita ser completado por ti mismo. Escribir el protocolo general en realidad significa que puedes acceder a la plataforma únicamente y completar la estrategia. FMZ oficial a veces libera la versión exe del protocolo general de plataformas. El protocolo general también se puede hacer en Python, que luego se puede ejecutar en el docker como un bot ordinario.

Introducción específica del protocolo general:https://www.fmz.com/bbs-topic/9120Ejemplo de escritura de protocolo general en Python:https://www.fmz.com/strategy/101399

Creando su propia plataforma cuantitativa

Al igual que varias operaciones de una plataforma se pueden implementar a través de API, el sitio web de FMZ también se basa en API. Puede solicitar su propia API-KEY del sitio web de FMZ para realizar funciones, como create, restart, DeleteRobot, GetRobotList GetRobotLogs, etc. Consulte la sección API Extension of FMZ Platform en el documento API para obtener detalles.

Debido a la poderosa extensibilidad de la plataforma FMZ Quant, puede crear su propia plataforma cuantitativa basada en la extensión API, permitiendo a sus usuarios ejecutar bots en su plataforma, etc. Referencia específica:https://www.fmz.com/bbs-topic/1697 .

Convertirse en socio de FMZ

Promoción del aula de NetEase

El mercado de comercio de criptomonedas ha atraído cada vez más la atención de los operadores cuantitativos debido a su particularidad. De hecho, el comercio de programas se ha convertido en la corriente principal de la criptomoneda, y estrategias como la cobertura y la creación de mercados siempre están activas en el mercado.www.fmz.com) actualmente la mayor comunidad y plataforma cuantitativa de criptomonedas, ha ayudado a miles de principiantes en el camino hacia el comercio cuantitativo durante más de 4 años.

Promoción deCurso de comercio cuantitativo de criptomonedas en NetEase Cloud Classroom. Inicie sesión en NetEase Cloud Classroom y comparta su enlace de curso (el enlace tiene una ID de curso única). Otros, que se registren y compren el curso a través de este enlace, le traerán el 50% del total como comisión, a saber, 10 yuanes. Siga la cuenta pública de WeChat de NetEase Cloud Classroom Premium Course Promotion para retirar el efectivo. También puede invitar a otros a promover el curso en Weibo o grupo QQ.

Afiliados

Los consumidores que hagan clic en el enlace de promoción, se registren y recarguen dentro de medio año, disfrutarán de la política de que nuestra compañía reembolsará según la cantidad efectiva en el orden válido. La comisión se devolverá a la cuenta del promotor en forma de puntos. Los usuarios pueden intercambiar los puntos al saldo de la cuenta de la plataforma FMZ en una proporción de 10: 1, y los usuarios también pueden usar los puntos para intercambiar los productos relacionados de FMZ Quant en el futuro.https://www.fmz.com/bbs-topic/3828

Plataforma cuántica FMZ para empresas

El sitio web completo de FMZ se puede implementar en el servidor exclusivo de una empresa o un equipo para un control completo y una personalización funcional. El sitio web de FMZ ha sido utilizado y probado por aproximadamente 100,000 usuarios, y ha logrado una alta disponibilidad y seguridad, lo que puede ahorrar tiempo para equipos cuantitativos y empresas. La versión empresarial es para equipos de comercio cuantitativos de tamaño mediano, proveedores de servicios de futuros de productos básicos, etc. Póngase en contacto con el administrador para obtener cotizaciones específicas.

Sistema de creación de mercado

El sistema profesional, que proporciona la liquidez del mercado y la gestión de fondos para las plataformas, podría ser el sistema de creación de mercado más mejorado del mercado.

Esquema de plataforma

El sistema de negociación de tecnología FMZ adopta tecnología de correspondencia de memoria, y la velocidad de procesamiento de pedidos es de hasta 2 millones de operaciones por segundo, lo que puede garantizar que no habrá retraso o retraso en el procesamiento de pedidos. Puede mantener el funcionamiento suave y estable de plataformas con más de 20 millones de usuarios en línea simultáneos. El marco del sistema de múltiples capas y múltiples grupos garantiza la seguridad, estabilidad y extensibilidad del sistema. La implementación de funciones y las actualizaciones de versiones se pueden llevar a cabo sin tiempo de inactividad, lo que garantiza la experiencia operativa máxima de los usuarios del terminal. En la actualidad, el sistema se puede experimentar en la plataforma simulada wex.app.


Más.