[TOC] ¿Qué quieres decir? 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.
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.Info
campo devuelve información en bruto, pero todavía no hace ninguna diferencia en el problema de no soportar interfaces.
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)
}
Para acceder a las interfaces públicas, Js puede utilizarHttpQuery
, y Python puede utilizar paquetes relacionados, tales comourllib
o 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()
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:
https://www.bitmex.com/api/explorer/
;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 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
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.
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;
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"]}')
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)
}
}
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,
En este caso, es obvio que simplemente usar
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
}
}
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
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)
}
}
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.
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
, GetAccount
y así sucesivamente.IO
Tambié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
En este momento, el marco general del programa se puede escribir como un modelo, similar al modelo
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
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__isStock
El 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
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
'''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()
exchange.SetData(arr) cambia la fuente de datos de backtest y utiliza datos de línea K personalizados. El parámetro
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
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.
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.
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
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
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 .
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
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
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.
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.
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.