Estrategias para la plataforma de cuantificación de FMZ y tutoriales de desarrollo

El autor:Las hierbas, Creado: 2019-08-19 15:54:35, Actualizado: 2021-06-08 16:14:57

[TOC] Antes de aprender este tutorial, hay que aprender.Introducción a la plataforma de cuantificación de los inventores de FMZyEstrategias para escribir tutoriales iniciales de la plataforma de cuantificación FMZTambién tiene experiencia en lenguaje de programación.El tutorial inicial trata de las funciones más comunes, pero hay muchas otras funciones y funciones que no se presentan y que no se cubrirán en este tutorial.Después de leer este tutorial, usted será capaz de escribir estrategias más libres y más personalizadas, y la plataforma FMZ es sólo una herramienta.

Acceso a los datos originales de las bolsas

La plataforma FMZ está envuelta en todos los intercambios compatibles, y para mantener la uniformidad, el soporte de la API de un solo intercambio no es completo. Por ejemplo, la obtención de K-Line generalmente puede transmitir el número de K-Lines o la hora de inicio, mientras que la plataforma FMZ es fija, algunas plataformas soportan pedidos por lotes, FMZ no lo soporta, etc. Por lo tanto, se necesita un método para acceder directamente a los datos de la bolsa.Se puede usar para interfaces abiertas (como la industria).HttpQuery, para agregar información de la cuenta, se necesita usarIOLos parámetros de entrada específicos también se pueden consultar en el documento de la API correspondiente.InfoEl campo devuelve la información original, pero todavía no puede resolver el problema de no soportar interfaces.

¿ Qué es eso?

Devuelve el contenido original (string) de la última solicitud de REST API, que se puede usar para analizar la extensión de información por sí 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 para acceder a la interfaz pública

Acceso a la interfaz pública, Js puede ser utilizadoHttpQueryPython puede utilizar paquetes relacionados, como:urllib¿Qué es esto?requests

HttpQuery es el método de GET por defecto, y también es compatible con más funciones, por ejemplo, ver la documentación de la API.

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

Ejemplos de Python con 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()

La función IO tiene acceso a las puertas cerradas

Para las interfaces que requieren una firma API-KEY, se puede usar la función IO, el usuario solo necesita preocuparse por los parámetros de entrada, y el proceso de firma específico se realizará en la base.

La plataforma FMZ no es compatible con BitMEX Stop Loss, pero puede implementarse a través de IO siguiendo los siguientes pasos:

  • En la página de instrucciones de la API de BitMEX:https://www.bitmex.com/api/explorer/
  • La siguiente dirección para encontrar BitMEX es:https://www.bitmex.com/api/v1/orderEl método esPOST◦ Como FMZ ya ha especificado la dirección de la raíz internamente, sólo hay que ingresar "/api/v1/order".
  • Parámetros correspondientessymbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=Stop

El código específico es:

var id = exchange.IO("api", "POST", "/api/v1/order", "symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=Stop")
//也可以直接传入对象
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 websocket

Básicamente, todos los intercambios de moneda digital soportan el mercado de envío de websocket, y algunos intercambios soportan el websocket para actualizar la información de la cuenta. En comparación con rest API, el websocket generalmente tiene un bajo tiempo de espera, una alta frecuencia, no tiene restricciones de frecuencia de la rest API de la plataforma.

Este artículo se centrará principalmente en la plataforma de cuantificación de inventores de FMZ, el uso del lenguaje JavaScript, la conexión con la función Dial envuelta en la plataforma, las especificaciones y parámetros en la documentación, la búsqueda de Dial, para implementar diversas funciones, la función Dial se actualizó varias veces, y este artículo cubrirá esto, y presentará las estrategias de conducción de eventos basadas en wss, así como los problemas de conexión de múltiples intercambios.

1.websocket连接

En general, se puede conectar directamente para obtener el ticker de seguridad de la moneda:

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

Para que los datos devueltos sean de formato comprimido, lo que se necesita en la conexión es lo que se especifica, compress especifica el formato de compresión, el modo representa el envío de los datos devueltos que se necesitan comprimir, como en la conexión 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, realizada por el lenguaje Go subyacente, la conexión de detección se desconecta y se vuelve a conectar, muy conveniente para el contenido de los datos solicitados que ya están en la url, como el ejemplo de Binance, que se recomienda usar.

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

Los mensajes de suscripción a WSS, las solicitudes de algunos intercambios están en la url, también hay algunos canales que requieren enviar sus propios mensajes de suscripción, como Coinbase:

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

2.加密接口连接

Generalmente se usa un websocket para leer transacciones, pero también se puede usar para obtener pedidos y envíos de cuentas, y el envío de este tipo de datos cifrados a veces puede tener un gran retraso, por lo que debe usarse con precaución. Debido a que los métodos de cifrado son más complejos, aquí se dan algunos ejemplos.

    //火币期货推送例子
    var ACCESSKEYID = '你的火币账户的accesskey'
    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} }") //去掉}}之间的多余空格
    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}))
        }
    }
    
    //币安推送例子,注意需要定时更新listenKey
    var APIKEY = '你的币安accesskey'
    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()
    }

    //BitMEX推送例子
    var APIKEY = "你的Bitmex API ID"
    var expires = parseInt(Date.now() / 1000) + 10
    var signature = exchange.HMAC("sha256", "hex", "GET/realtime" + expires, "{{secretkey} }")//secretkey在执行时自动替换,不用填写
    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读取

En general, en el ciclo de la muerte se puede leer continuamente, el código es el siguiente:

function main() {
    var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr");
    while (true) {
        var msg = client.read()
        var data = JSON.parse(msg) //把json字符串解析为可引用的object
// 处理data数据
    }
}

La velocidad de transferencia de datos de wss es muy rápida, la parte inferior de Go retrasa todos los datos en la cola, y cuando el programa llama read, vuelve a hacerlo; mientras que las operaciones de descarga del disco físico provocan un retraso y pueden causar acumulación de datos. Para transferencias de transacciones, transferencias de cuentas, transferencias de inserción de profundidad, etc., necesitamos datos históricos, para datos de mercado, en la mayoría de los casos nos preocupamos solo por lo último, sin importar los datos históricos.

read()Si no se agregan parámetros, se devuelven los datos más antiguos y se bloquean para regresar cuando no hay datos.client.read(-2)Devolverá los datos más recientes de inmediato, pero volverá a cero cuando no haya más datos, lo que requiere una nueva referencia.

Dependiendo de cómo se trate los datos viejos almacenados en el caché y si se bloquean cuando no hay datos, read tiene diferentes parámetros, como se muestra a continuación, que parecen complicados, pero que hacen que el programa sea más flexible.img

4.连接多个交易所websocket

Para este caso, el procedimiento no puede utilizarse con un simple read (), ya que un intercambio bloqueará el mensaje en espera, en este caso, otro intercambio no recibirá el mensaje aunque haya un nuevo mensaje. El procedimiento general es:

    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) // 参数-1代表无数据立即返回null,不会阻塞到有数据返回
            var msgCoinbase = coinbase.read(-1)
            if(msgBinance){
                // 此时币安有数据返回
            }
            if(msgCoinbase){
                // 此时coinbase有数据返回
            }
            Sleep(1) // 可以休眠1ms
        }
    }

5.断线重连问题

Esta parte de la gestión es más problemática, ya que los datos de impulso pueden ser interrumpidos, o el tiempo de impulso es extremadamente alto, incluso si se puede recibir un latido cardíaco, no significa que los datos estén siendo impulsados, se puede establecer un intervalo de eventos, se vuelve a conectar si no se recibe ninguna actualización si se supera el intervalo, y lo mejor es que se comparen los resultados de retorno de un tiempo y rest para ver si los datos son exactos.

6.使用websocket的一般程序框架

Debido a que ya se han utilizado los datos de empuje, los programas también se escriben como event drivers, teniendo en cuenta que se empujan datos con frecuencia, sin que se produzcan demasiadas solicitudes que provoquen el cierre, generalmente se puede escribir:

    var tradeTime = Date.now()
    var accountTime = Date.now()
    function trade(data){
        if(Date.now() - tradeTime > 2000){//这里即限制了2s内只交易一次
            tradeTime = Date.now()
            //交易逻辑
        }
    }
    function GetAccount(){
        if(Date.now() - accountTime > 5000){//这里即限制了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)
        }
    }

7.总结

El modo de conexión del websocket, el modo de envío de datos, el contenido suscriptible y el formato de datos de los diferentes intercambios suelen ser diferentes, por lo que la plataforma no se envuelve y se requiere una conexión automática con la función Dial.

PS. Algunos intercambios, aunque no ofrecen el mercado de websocket, en realidad utilizan la función de tono para acceder a su sitio web.

Conjunción de múltiples hilos

JavaScript puede implementarse en paralelo con la función Go, y Python puede utilizar una biblioteca de múltiples hilos correspondiente.

En la implementación de estrategias de cuantificación, en muchos casos, la ejecución simultánea puede reducir la eficiencia de la mejora de la demora.

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

Una vez que se solicita una API rest, hay un retraso, suponiendo que sea de 100 ms, entonces el tiempo de obtención de profundidad en dos ocasiones es realmente diferente, y si se requieren más visitas, el problema de retraso será más prominente y afectará la ejecución de la política.

JavaScript no tiene muchos hilos, por lo que la función Go envuelta en el fondo resuelve este problema.GetDepth,GetAccountPor ejemplo.IOEn la página web de Facebook, se puede leer:exchange.Go("IO", "api", "POST", "/api/v1/contract_batchorder", "orders_data=" + JSON.stringify(orders))Sin embargo, debido al mecanismo de diseño, es más complicado de implementar.

var a = exchanges[0].Go("GetDepth")
var b = exchanges[1].Go("GetDepth")
var depthA = a.wait() //调用wait方法等待返回异步获取depth结果 
var depthB = b.wait()

En la mayoría de los casos sencillos, escribir una política así no es un problema. Sin embargo, tenga en cuenta que cada ciclo de política debe repetir este proceso, y las variables intermedias a, b son en realidad solo auxiliares temporales. Si tenemos muchas tareas simultáneas, también se debe registrar la relación de correspondencia entre a y depthA, b y depthB, lo que hace que la situación sea más compleja cuando nuestras tareas simultáneas no están determinadas. Por lo tanto, queremos implementar una función: cuando se escribe Go simultáneamente, se une una variable al mismo tiempo, y cuando se devuelve el resultado de la ejecución simultánea, el resultado se asigna automáticamente a la variable, lo que elimina las variables intermedias y hace que el programa sea más sencillo.

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

Definimos una función G, donde el parámetro t es la función Go a ejecutar, ctx es el contexto del programa de registro, y f es la función con la asignación específica.

En este caso, el marco de programación general puede escribirse de manera similar al modelo de programación productor-consumidor (con algunas diferencias), donde el productor emite continuamente tareas y el consumidor las ejecuta simultáneamente, mientras que el código es solo una demostración y no involucra la lógica de ejecución del programa.

var Info = [{depth:null, account:null}, {depth:null, account:null}] //加入我们需要获取两个交易所的深度和账户,跟多的信息也可以放入,如订单Id,状态等。
var tasks = [ ] //全局的任务列表

function produce(){ //下发各种并发任务
  //这里省略了任务产生的逻辑,仅为演示
  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 //这里的v就是并发Go函数wait()的返回值,可以仔细体会下
                }))
    }
    _.each(jobs, function(t){
            t.run() //在这里并发执行所有任务
        })
    tasks = []
}
function main() {
    while(true){
        produce()         // 发出交易指令
        worker()        // 并发执行
        Sleep(1000)
    }
}

Lo que parece ser una vuelta de tuerca es la realización de una función simple, que en realidad simplifica mucho la complejidad del código, ya que solo tenemos que preocuparnos por qué tareas necesita producir el programa, y el programa worker () los ejecuta automáticamente en paralelo y devuelve los resultados correspondientes.

Diagrama de la función Chart

El tutorial de iniciación a la introducción de gráficos es una biblioteca de gráficos recomendada, que en la mayoría de los casos puede satisfacer las necesidades. Si se necesita una mayor personalización, se puede operar directamente con objetos de gráficos.

Chart({…})Los parámetros internos son los objetos HighStock y HighCharts, sólo añadiendo un parámetro adicional__isStockPara distinguir si es o no HighStock. HighStock se centra más en la secuencia de tiempo y, por lo tanto, se usa más a menudo. FMZ admite básicamente los módulos básicos de HighCharts y HighStock, pero no admite módulos adicionales.

Los ejemplos concretos de HighCharts:https://www.highcharts.com/demoEl ejemplo de HighStock:https://www.highcharts.com/stock/demoPor ejemplo, el código de estos ejemplos puede ser fácilmente transferido a FMZ.

Se puede llamar add (([series index ((como 0, datos]) para agregar datos a la serie del índice especificado, se llama reset (() datos de gráfico en blanco, reset puede llevar un parámetro numérico para especificar la entrada reservada. Soporta mostrar múltiples gráficos, la configuración solo requiere la transmisión de parámetros de la matriz, como: var chart = Chart (([{...}, {...}, {...})), por ejemplo, un gráfico tiene dos series, un gráfico tiene una serie, un gráfico tiene tres series, entonces cuando se especifica una serie con 01, la serie ID representa los dos datos de la serie 1 de la tabla, cuando se especifica una serie ID representa los datos de la serie 1 de la tabla, cuando se especifica la serie ID, la serie 2 indica los datos de la primera serie de la tabla 2, y la serie 3 indica los datos de la primera serie de la tabla 3.

Un ejemplo concreto:

var chart = { // 这个 chart 在JS 语言中 是对象, 在使用Chart 函数之前我们需要声明一个配置图表的对象变量chart。
    __isStock: true,                                    // 标记是否为一般图表,有兴趣的可以改成 false 运行看看。
    tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'},    // 缩放工具
    title : { text : '差价分析图'},                       // 标题
    rangeSelector: {                                    // 选择范围
        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'},                         // 坐标轴横轴 即:x轴, 当前设置的类型是 :时间
    yAxis : {                                           // 坐标轴纵轴 即:y轴, 默认数值随数据大小调整。
        title: {text: '差价'},                           // 标题
        opposite: false,                                // 是否启用右边纵轴
    },
    series : [                                          // 数据系列,该属性保存的是 各个 数据系列(线, K线图, 标签等..)
        {name : "line1", id : "线1,buy1Price", data : []},  // 索引为0, data 数组内存放的是该索引系列的 数据
        {name : "line2", id : "线2,lastPrice", dashStyle : 'shortdash', data : []}, // 索引为1,设置了dashStyle : 'shortdash' 即:设置 虚线。
    ]
};
function main(){
    var ObjChart = Chart(chart);  // 调用 Chart 函数,初始化 图表。
    ObjChart.reset();             // 清空
    while(true){
        var nowTime = new Date().getTime();   // 获取本次轮询的 时间戳,  即一个 毫秒 的时间戳。用来确定写入到图表的X轴的位置。
        var ticker = _C(exchange.GetTicker);  // 获取行情数据
        var buy1Price = ticker.Buy;           // 从行情数据的返回值取得 买一价
        var lastPrice = ticker.Last + 1;      // 取得最后成交价,为了2条线不重合在一起 ,我们加1
        ObjChart.add([0, [nowTime, buy1Price]]); // 用时间戳作为X值, 买一价 作为Y值 传入 索引0 的数据序列。
        ObjChart.add([1, [nowTime, lastPrice]]); // 同上。
        Sleep(2000);
    }
}

Un ejemplo de un diseño gráfico:https://www.fmz.com/strategy/136056

Reevaluación de la progresión

Python hace una repetición local

La dirección de código abierto es:https://github.com/fmzquant/backtest_python

Instalaciones

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

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

Un ejemplo simple

Los parámetros de retorno se establecen en forma de comentarios al comienzo del código de la política, por ejemplo, en la interfaz de edición de la política 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

Las pruebas

Debido a que la estrategia completa requiere un ciclo muerto, los EOF anormales se lanzarán para terminar el procedimiento al finalizar la retrospección, por lo que se debe tolerar errores.

# !/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__

# ------------------------------ 策略部分开始 --------------------------

print exchange.GetAccount()     # 调用一些接口,打印其返回值。
print exchange.GetTicker()

def adjustFloat(v):             # 策略中自定义的函数
    v = math.floor(v * 1000)
    return v / 1000

def onTick():
    Log("onTick")
    # 具体的策略代码


def main():
    InitAccount = GetAccount()
    while True:
        onTick()
        Sleep(1000)

# ------------------------------ 策略部分结束 --------------------------

try:
    main()                     # 回测结束时会 raise EOFError() 抛出异常,来停止回测的循环。所以要对这个异常处理,在检测到抛出的异常后调用 task.Join() 打印回测结果。
except:
    print task.Join()         

Datos personalizados de retrospección

exchange.SetData ((arr), para cambiar la fuente de datos de repetición con datos de línea K personalizados. Arr, un elemento de un conjunto de datos de columnas de línea K (es decir, un conjunto de datos de línea K, que solo soporta repetición JavaScript).

En arr, el formato de datos de los elementos individuales es:

[
    1530460800,    // time     时间戳
    2841.5795,     // open     开盘价
    2845.6801,     // high     最高价
    2756.815,      // low      最低价
    2775.557,      // close    收盘价
    137035034      // volume   成交量
]

Los fuentes de datos pueden ser importados a la librería de modelos.

function init() {                                                          // 模板中的 init 初始化函数会在加载模板时,首先执行,确保 exchange.SetData(arr) 函数先执行,初始化,设置数据给回测系统。
    var arr = [                                                            // 回测的时候需要使用的K线数据
        [1530460800,2841.5795,2845.6801,2756.815,2775.557,137035034],      // 时间最早的一根 K线柱 数据
        ... ,                                                              // K线数据太长,用 ... 表示,数据此处省略。
        [1542556800,2681.8988,2703.5116,2674.1781,2703.5116,231662827]     // 时间最近的一根 K线柱 数据
    ]
    exchange.SetData(arr)                                                  // 导入上述 自定义的数据
    Log("导入数据成功")
}

Nota: Es necesario que se introduzcan los datos personalizados (es decir, se llaman datos de configuración de la función exchange.SetData) al iniciar, y los ciclos de datos de línea K personalizados deben coincidir con los ciclos de línea K de la base establecidos en la página de recuperación, es decir, si los datos de línea K personalizados tienen un tiempo de línea K de 1 minuto, los ciclos de línea K de la base establecidos en la página de recuperación también deben ser de 1 minuto.

Utiliza las bolsas que no son compatibles con FMZ

Si el intercambio no soportado y el intercambio ya soportado son exactamente iguales en API, sólo que la dirección de base es diferente, puede ser soportado por el modo de cambio de dirección de base. Específicamente, para agregar un intercambio, seleccione el intercambio soportado, pero API-KEY llene el intercambio no soportado, en la política, use IO para cambiar la dirección de base, como:

exchange.IO("base", "http://api.huobi.pro") 
//http://api.huobi.pro为为支持交易所API基地址,注意不用添加/api/v3之类的,会自动补全

Aunque no todos los intercambios soportan FMZ, la plataforma ofrece acceso al protocolo general.

  • Si escribe su propio código para acceder a la bolsa, el programa creará un servicio en línea.
  • Añadir un intercambio en la plataforma FMZ, especificar la dirección y el puerto de los servicios de la red.
  • Cuando el administrador está operando en el disco real de un intercambio con un protocolo general, los accesos a la API en la política se envían al protocolo general.
  • Los protocolos generales visitan los intercambios a petición y devuelven los resultados a los custodios.

En pocas palabras, el protocolo general es equivalente a un intermediario que representa las solicitudes de los administradores y devuelve los datos de acuerdo con los estándares correspondientes. El código del protocolo general requiere que se complete por sí mismo, y escribir el protocolo general en realidad significa que puede acceder por separado a los intercambios y completar las políticas. Los oficiales de FMZ a veces publican una versión exe del protocolo general del intercambio.

El acuerdo fue firmado por el gobierno de la República Popular China.https://www.fmz.com/bbs-topic/1052Un ejemplo de un protocolo general escrito en Python:https://www.fmz.com/strategy/101399

Crear su propia plataforma de cuantificación

Al igual que en las bolsas, todas las operaciones se pueden realizar a través de la API, el sitio web de FMZ también está basado en la API. Puede solicitar su propio sitio web de FMZ API-KEY para implementar funciones como crear, reiniciar, eliminar el disco real, acceder a la lista del disco real, acceder a los registros del disco real, etc.

Debido a la gran extensibilidad de la plataforma FMZ, puedes crear tu propia plataforma cuantitativa basada en la API de extensión, para que los usuarios puedan ejecutar discos físicos en tu plataforma.

¿Cómo ser socio de FMZ?

La promoción de las clases en la nube

El mercado de comercio de divisas digitales es cada vez más objeto de atención por parte de los comerciantes cuantificados debido a sus particularidades. De hecho, el comercio programado ya es el principal flujo de divisas digitales, y estrategias como el mercado de cobertura están siempre activos en el mercado.www.fmz.comEl curso, que cuesta solo 20 dólares y está dirigido a principiantes, lleva más de cuatro años ayudando a miles de principiantes a tomar el camino de la transacción cuantitativa.

La difusiónCurso de transacción cuantitativa de monedas digitales en el aula de la nubeEntrar en la clase en la nube, compartir el enlace de su curso (enlace con el exclusivo curso ID), y otros se registran y compran el curso a través de este enlace, obtendrá un 50% de 10 yuanes en total.

Promoción de la reelección

Los consumidores hacen clic en el enlace de promoción y se reabastecen en el plazo de seis meses, y nuestra división devuelve la comisión según la cantidad efectiva en el pedido válido. La comisión se devuelve en forma de puntos a la cuenta del promotor, y los usuarios pueden cambiar el inventor por el saldo de la cuenta de la plataforma de negociación cuantificada a una proporción de 10: 1, o pueden cambiar el inventor por el producto en torno a la cantidad de productos cuantificados con puntos en un futuro.https://www.fmz.com/bbs-topic/3828

Edición empresarial de la plataforma de cuantificación FMZ

Se puede implementar el sitio web completo de FMZ en el servidor exclusivo de una empresa o equipo, para lograr un control y una funcionalidad completamente personalizados. El sitio web de FMZ ha sido utilizado y probado por aproximadamente 100,000 usuarios, alcanzando una alta disponibilidad y seguridad, ahorrando tiempo y costos de tiempo para equipos y empresas de cuantificación.

El sistema de la ciudad

El sistema especializado para proporcionar liquidez de mercado y gestión de fondos a los intercambios es probablemente el sistema de mercado más completo del mercado y es utilizado por muchos intercambios y equipos.

El programa de las bolsas

El sistema de negociación de la tecnología de los inventores utiliza tecnología de captura de memoria, la velocidad de procesamiento de pedidos de hasta 2 millones de copias / segundo, que puede garantizar que el procesamiento de pedidos no se produce ninguna demora y cartón. Puede mantener al mismo tiempo el fluido y el funcionamiento estable de los intercambios con un número de usuarios en línea superior a 20 millones. La arquitectura del sistema de múltiples capas y clusters garantiza la seguridad del sistema, la estabilidad, la escalabilidad.


Más.

Se casó en 2009¿Puedo seguir en el grupo?

- ¿ Qué?¡Dios de la hierba, ¡muy bien!

Las hierbas¿Por qué no lo haces? // Ejemplo de empuje de futuros de tokens var ACCESSKEYID = 'la clave de acceso de su cuenta de tokens' var apiClient = Dial (('wss://api.hbdm.com/notification debe comprimir=gzip&mode=recv') var date = new Date ((); y var date = new Date (); y var date = new Date (); y var date (); var now_utc = Date.UTC ((date.getUTCFullYear ((), date.getUTCMonth ((), date.getUTCDate ((), date.getUTCHours ((), date.getUTCMinutes ((), date.getUTCSeconds (())); var utc_date = nueva fecha (now_utc) var Timestamp = utc_date.toISOString (().substring ((0,19) el tiempo de inicio y el final de la sesión var quest = 'GET\napi.hbdm.com\n/notification\n'+'AccessKeyId='+ACCESSKEYID+'&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=' + encodeURIComponent ((Timestamp) y el código de código de la clave de acceso es el siguiente: var signature = exchange.HMAC (("sha256", "base64", quest, "{{secretkey}}")) es un código de código abierto que permite a los usuarios de código abierto y abierto de usar una clave secreta. ¿Por qué no lo haces?

División de BatallaPor favor, si el websocket se conecta a un mercado de suscripción, entonces se obtiene el mercado de datos con la función read ((), ¿verdad? Si se utiliza el exchange.GetTicker (()) no se extrae el dato de la caché local, sino que se inicia una solicitud rest que devuelve el dato, ¿verdad? Solo una familia de tokens puede cambiar el modo en que el mercado acepta a través de exchange.IO (WEB y luego utilizar exchange.GetTicker (WEB y exchange.GetDepth (WEB para no solicitar datos al resto del mercado y obtener datos de los mercados de suscripción ya recibidos que existen en el búfer local. Lo entiendo, ¿cierto?

Las hierbasSigue con la verificación de las sugerencias

¿Qué quieres decir?Por favor, la información de verificación del grupo de FMZ ahora es:

ShaltielEstá bien.

Las hierbasSí, lo mejor es que todos los websockets utilicen Dial para un control más intuitivo.