[TOC] ¿Qué quieres decir?
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.
Puede operar en múltiples intercambios y múltiples símbolos dentro de un robot fácilmente.
exchange.GetTicker()
cuando se añade un intercambioexchanges[0].GetTicker()
, exchanges[1].GetTicker()
exchange
mediante el uso deIO
Funciónvar 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)
}
}
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.
La imagen a continuación muestra cómo configurar el símbolo de futuros en BTC al iniciar el bot.
A continuación se muestra cómo establecer un tipo de contrato para cada intercambio.
exchange.SetContractType("swap")
exchange.SetContractType("this_week")
exchange.SetContractType("next_week")
exchange.SetContractType("quarter")
exchange.SetContractType("this_week")
exchange.SetContractType("next_week")
exchange.SetContractType("quarter")
exchange.SetContractType("XBTUSD")
exchange.SetContractType("XBTM19")
exchange.SetContractType("swap")
exchange.SetContractType("BTC-PERPETUAL")
exchange.SetContractType("BTC-27APR18")
Introducción básica
FMZ tiene dos modos de backtesting:real tick
ysimulate 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:Puntos ocultos:
Resultado de la prueba posterior
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ánull
Así 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:Tambié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
}
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 sobreHttpQuery
En 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.
HttpQuery
devuelve 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,HttpQuery
Es una función muy útil.HttpQuery
sólo admite JavaScript, para Python, utilizando elurlib2
o bienrequest
biblioteca para enviar solicitudes http directamente.
- ¿ Qué?
Para esas API privadas, usandoHttpQuery
será muy complicado porque usted necesita para tratar con API-clave, signo, hash, etc.IO
es una función útil para esta condición, comprobar enSe trata de una aplicación de software para la gestión de datos.. IO
En 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.
POST
, 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=StopEl código JavaScript final:
var id = exchange.IO("api", "POST", "/api/v1/order", "symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=Stop")
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 usarDial
Función para conectar a websocket, Para Python, se puede utilizarDial
o bienwebsocket_client
libray.
Este tutorial se centrará en la conexión de websockets utilizando el JavaScript yDial
La 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
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
compress
significa que los datos están en formato comprimido, y el parámetromode
representa 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")
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr?reconnect=true")
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 llamaread
Las operaciones de red del robot, tales comoBuy
,GetAccount
,CancelOrder
Para 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 devolvernull
si 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 serdual significa que tanto el envío como el recibimiento deben comprimirse,send significa enviar necesidad de ser comprimido yrecv significa recibir. |
Proxy (procesión) | Configuración de proxy para ss5.socks5://name:pwd@192.168.0.1:1080 |
vuelve a conectar | Reconnect=true para permitir la reconexión |
el intervalo | interval es 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. | regresonull inmediatamente. |
regresonull inmediatamente. |
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()
}
Puede que hayas notado que todos los códigos que tenemos ahora son de hilo único, ejecución secuencial.GO
En 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.Go
cuando 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ésGo
Función, de lo contrario, el recurso de hilo se acumulará hasta 2000 y devolverá un error.
LogStatus
y 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__isStock
atributo que no existe en el original.__isStock
es falso, el gráfico se mostrará como HighCharts.__isStock
si 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
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.Las 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
el nombre de la entidad- Gracias.
Las hierbasEstoy trabajando en este tutorial. Tomará unos días completarlo. Siéntase libre de hacer cualquier pregunta.