[TOC] Antes de aprender este tutorial, hay que aprender.Introducción a la plataforma de cuantificación de los inventores de FMZyLa estrategia de la plataforma de cuantificación FMZ para escribir un tutorial para principiantesTambién es un experto en lenguajes de programación.El tutorial para principiantes aborda las funciones más usadas, pero hay muchas otras funciones y funcionalidades que no se describen en este tutorial, y es necesario consultar la documentación de la API de la plataforma para entenderlas por sí mismo.Después de aprender este tutorial, serás capaz de escribir estrategias más libres y personalizadas, y la plataforma FMZ no es más que una herramienta.
La plataforma FMZ es un envase de todas las plataformas de intercambio compatibles. Para mantener la uniformidad, el soporte de la API de una sola plataforma no es completo. Por ejemplo, la obtención de la línea K generalmente puede transmitirse en la cantidad de líneas K o la hora de inicio, mientras que la plataforma FMZ es fija, algunas plataformas admiten pedidos en serie, FMZ no es compatible, etc. Por lo tanto, se necesita un método para acceder directamente a los datos de la plataforma.Para las interfaces públicas (si es el caso), se puede usarHttpQueryPara el acceso a la información de la cuenta, se necesita usarIO。Los parámetros de entrada específicos también se pueden consultar en la documentación de la API de la bolsa correspondiente.InfoEl campo devuelve la información original, pero no resuelve el problema de no soportar la interfaz.
Regresa el contenido original de la última solicitud de REST API (string), que se puede usar para descifrar la información de extensión 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)
}
Acceso a la interfaz pública, Js puede ser utilizadoHttpQueryEn Python, se puede usar el paquete por sí mismo, como por ejemplo:urlliborequests。
HttpQuery es el método GET por defecto, y soporta más funciones, 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 usando solicitudes
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 la 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 se realizará por la capa inferior.
La plataforma FMZ no soporta actualmente los Stop Loss Orders de BitMEX, que se implementan a través de IO siguiendo los siguientes pasos:
https://www.bitmex.com/api/explorer/。https://www.bitmex.com/api/v1/orderEl método es:POST。 Debido a que FMZ ya ha especificado la dirección de la raíz internamente, solo tiene que introducir “/api/v1/order”.symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=StopEl código es el siguiente:
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
Básicamente, todos los exchanges de moneda digital soportan el envío de websocket, algunos exchanges soportan la información de la cuenta de actualización de websocket. En comparación con la rest API, la websocket generalmente tiene una baja latencia, una alta frecuencia, no está limitada por la frecuencia de la plataforma rest API, etc.
Este artículo se enfocará principalmente en la plataforma de cuantificación de los inventores de FMZ, que utiliza el lenguaje JavaScript para conectar con la función Dial que se encierra en la plataforma. Las especificaciones y los parámetros están en la documentación, la búsqueda de Dial, la función Dial se ha actualizado varias veces para implementar diversas funciones. Este artículo cubrirá esto y presentará las estrategias de accionamiento de eventos basadas en wss, así como los problemas de conexión de múltiples exchanges.
Por lo general, las conexiones son directas, por ejemplo, para obtener un ticker de seguridad de la moneda:
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
Para que los datos devueltos sean en formato comprimido, es necesario especificar el formato comprimido en la conexión, comprimir especifica el formato comprimido, y el modo representa el envío de los datos devueltos que requieren compresión, 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 soporta la reconexión, realizada por el lenguaje Go de nivel inferior, la detección de la conexión interrumpida y la reconexión de la reunión, es conveniente para el contenido de los datos de la solicitud ya en la url, como el ejemplo de Binance. Se recomienda su uso. Para aquellos que necesitan enviar mensajes por encargo, puede mantener su propio mecanismo de reconexión.
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr|reconnect=true")
Para suscribirse a las noticias de wss, algunos intercambios solicitan en la url, y algunos canales necesitan suscripciones propias, como coinbase:
client = Dial("wss://ws-feed.pro.coinbase.com", 60)
client.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')
El uso de websocket para leer las instrucciones es común, pero también se puede usar para obtener pedidos y envíos de cuentas. El envío de este tipo de datos encriptados a veces tiene un gran retraso, por lo que debe usarse con precaución. Debido a que el método de encriptación es más complejo, aquí se dan algunos ejemplos de referencia.
//火币期货推送例子
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)
}
}
En general, se puede leer continuamente en el ciclo de muerte, y 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 envío de datos de wss es muy rápida, la base de Go guarda todos los datos en una cola, y cuando un programa llama a read, los devuelve en secuencia. Mientras que las operaciones de pedido en el disco duro traen un retraso, lo que puede causar acumulación de datos. Para envío de transacciones, envío de cuentas, envío de inserciones de profundidad, etc., necesitamos datos históricos, y para datos reales, en la mayoría de los casos solo nos preocupamos por los últimos, no nos preocupamos por los datos históricos.
read()Si no se añade un parámetro, se devuelve el dato más antiguo, y si no hay datos se bloquea la devolución. Si se desea el dato más reciente, se puede usarclient.read(-2)Regresa los datos más recientes de inmediato, pero vuelve null cuando no hay más datos, y se necesita un juicio para volver a citar.
Dependiendo de cómo se traten los datos antiguos almacenados en la caché, y si se bloquean cuando no hay datos, read tiene diferentes parámetros, como se muestra a continuación, que parecen complicados, pero hacen que el programa sea más flexible.

Para este tipo de situaciones, el procedimiento no puede utilizar el simple read (), ya que una plataforma bloqueará el mensaje en espera, mientras que la otra plataforma no recibirá el mensaje, incluso si hay 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
}
}
Esta parte del procesamiento es un poco problemática, ya que el envío de datos puede interrumpirse o el retraso en el envío es muy alto, incluso si se puede recibir el latido del corazón no significa que los datos se estén enviando, se puede configurar un intervalo de eventos, si no se reciben actualizaciones después del intervalo, se vuelve a conectar, y es mejor comparar el resultado con el resto devuelto a intervalos de tiempo para ver si los datos son precisos. Para esta situación especial, se puede configurar directamente la reconexión automática.
Como ya se han utilizado datos de empuje, el programa también debe escribirse como un evento, teniendo en cuenta que los datos de empuje son frecuentes y no se bloquean con demasiadas solicitudes, por lo general se puede escribir como:
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)
}
}
El modo de conexión, el modo de envío de datos, el contenido de suscripción y el formato de datos de los websockets de las distintas plataformas suelen ser diferentes, por lo que las plataformas no se envasan, sino que se conectan por sí mismas con la función Dial. Este artículo cubre básicamente algunas de las precauciones básicas.
PS. Algunos intercambios, aunque no ofrecen la configuración de websocket, en realidad usan la función de configuración al acceder al sitio web y descubren que se utiliza el envío de websocket, si se investiga, se encuentra el formato de suscripción y el formato de retorno. Algunos parecen cifrados y se pueden ver descifrados con base64 .
JavaScript se puede implementar en paralelo a través de la función Go, y Python puede usar la correspondiente biblioteca multithread.
En muchos casos, la ejecución simultánea puede reducir la eficiencia de la mejora del tiempo de espera en la implementación de estrategias de cuantificación. Por ejemplo, en el disco duro de la estrategia de cobertura, se necesita obtener la profundidad de dos monedas, y el código ejecutado en la secuencia es el siguiente:
var depthA = exchanges[0].GetDepth()
var depthB = exchanges[1].GetDepth()
Hay un retraso en la solicitud de una API de descanso. Supongamos que es de 100 ms. En ese caso, el tiempo para obtener la profundidad dos veces es realmente diferente. Si se requiere más acceso, el problema del retraso será más evidente y afectará la ejecución de la estrategia.
Como JavaScript no tiene múltiples hilos, el substrato envuelve la función Go para resolver este problema. La función Go se puede usar para API que requieren acceso a la red, comoGetDepth,GetAccountTambién apoyaIOSe llama:exchange.Go("IO", "api", "POST", "/api/v1/contract_batchorder", "orders_data=" + JSON.stringify(orders))Sin embargo, debido a los mecanismos de diseño, la implementación es más complicada.
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 simples, no hay nada de malo en redactar la política de esta manera. Pero tenga en cuenta que este proceso debe repetirse cada vez que se repite la estrategia, y las variables intermedias a y b son en realidad sólo ayudas temporales. Si tenemos muchas tareas simultáneas, necesitamos registrar la correspondencia entre a y profundidad A, y b y profundidad B. Cuando nuestras tareas simultáneas son inciertas, la situación se vuelve más complicada. Por lo tanto, queremos implementar una función: al escribir concurrencia en Go, vincular una variable al mismo tiempo, y cuando se devuelve el resultado de la ejecución concurrente, el resultado se asigna automáticamente a la variable, eliminando así las variables intermedias y haciendo que el programa sea más conciso. La implementación específica es la siguiente:
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 y f es la función para la asignación específica. Veremos esta función en acción en breve.
En este punto, el marco general del programa se puede escribir como un modelo de “productor-consumidor” (con algunas diferencias), donde los productores emiten tareas continuamente y los consumidores las ejecutan simultáneamente. El código siguiente es solo para demostración y no involucra al programa. Ejecutar lógica.
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)
}
}
Parece que solo hemos implementado una función simple después de realizar muchos pasos, pero, de hecho, la complejidad del código se ha simplificado enormemente. Solo debemos preocuparnos por las tareas que el programa necesita generar y el trabajador( ) el programa los ejecutará automáticamente de forma simultánea y devolverá los resultados correspondientes. La flexibilidad se ha mejorado mucho.
El tutorial para principiantes presenta el gráfico como una biblioteca de gráficos recomendada, que en la mayoría de los casos puede satisfacer las necesidades. Si se necesita una personalización adicional, se puede manipular directamente el objeto de gráfico.
Chart({…})Los parámetros internos son los objetos HighStock y HighCharts, sólo que se añade un parámetro adicional__isStockPara distinguir si es HighStock. HighStock se centra más en los gráficos de secuencias temporales, por lo que es más común usar. FMZ es compatible con los módulos básicos de HighCharts y HighStock, pero no con los módulos adicionales.
Ejemplos concretos de HighCharts: https://www.highcharts.com/demo; Ejemplos de HighStock: https://www.highcharts.com/stock/demo 。 Consulte el código de estos ejemplos para su fácil portabilidad a FMZ。
Se puede llamar add ().[series índice (como 0), datos]) agregar datos a series especificados en el índice, llamar a reset (como) vaciar datos de gráfico, reset puede llevar un parámetro numérico, especificar el número de líneas reservadas. Soporta la visualización de múltiples gráficos, y la configuración solo requiere pasar los parámetros de la serie, como: var chart = Chart (como)[{…}, {…}, {…}]), por ejemplo, si el gráfico 1 tiene dos series, el gráfico 2 tiene una serie, y el gráfico 3 tiene una serie, entonces cuando se especifica que los ID de la secuencia 0 y 1 representan los datos de las dos series de la actualización del gráfico 1, cuando se especifica que el ID de la secuencia 2 representa los datos de la primera serie del gráfico 2, cuando se especifica que la secuencia 3 se refiere a los datos de la primera serie del gráfico 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 diseño gráfico: https://www.fmz.com/strategy/136056
El código abierto es: https://github.com/fmzquant/backtest_python
Instalado
En la línea de comandos, escriba:
pip install https://github.com/fmzquant/backtest_python/archive/master.zip
Ejemplos simples
Los parámetros de retroalimentación se configuran como comentarios al comienzo del código de la política. Para más información, consulte la interfaz de edición de políticas del sitio web de FMZ para guardar la configuración de retroalimentación.
'''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 retrospectiva
Dado que la estrategia completa requiere un ciclo muerto, se lanzará una excepción de EOF para terminar el procedimiento después de la respuesta, por lo que se necesita tolerancia a los 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()
exchange.SetData(arr), cambia de fuente de datos de retroalimentación, con datos de línea K personalizados. El parámetro arr, un conjunto de datos de columna de línea K como elemento (((es decir, un conjunto de datos de línea K que, de momento, solo soporta retroalimentación en JavaScript。
El formato de datos de los elementos individuales en el array arr es:
[
1530460800, // time 时间戳
2841.5795, // open 开盘价
2845.6801, // high 最高价
2756.815, // low 最低价
2775.557, // close 收盘价
137035034 // volume 成交量
]
Las fuentes de datos se pueden importar en el repositorio de la clase de plantillas.
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: Cuando se inicializa, primero se debe importar los datos personalizados (es decir, los datos de configuración de la función exchange.SetData), el ciclo de datos de línea K personalizado debe coincidir con el ciclo de línea K subyacente establecido en la página de resonancia, es decir, los datos de línea K personalizados, el tiempo de una línea K es de 1 minuto, entonces el ciclo de línea K subyacente establecido en la resonancia también debe ser de 1 minuto.
Si el intercambio no soportado y el intercambio soportado tienen la misma API, solo que la dirección de base es diferente, se puede soportar mediante el cambio de dirección de base. Para agregar el intercambio, seleccione el intercambio soportado, pero rellene el API-KEY para el intercambio no soportado, use el IO en la estrategia para cambiar la dirección de base, como:
exchange.IO("base", "http://api.huobi.pro")
//http://api.huobi.pro为为支持交易所API基地址,注意不用添加/api/v3之类的,会自动补全
No todos los exchanges soportan FMZ, pero la plataforma ofrece acceso a los protocolos generales.
En pocas palabras, el protocolo general equivale 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 debe completarse por sí mismo, y escribir el protocolo general en realidad representa que puede acceder al intercambio por separado y completar la estrategia.
El proyecto de acuerdo incluye: https://www.fmz.com/bbs-topic/1052 Ejemplo de un protocolo general escrito en Python: https://www.fmz.com/strategy/101399
Al igual que las operaciones de las bolsas se pueden implementar a través de la API, el sitio web de FMZ también está basado en la API. Puede solicitar su propia implementación de la API-KEY del sitio web de FMZ, como crear, reiniciar, eliminar discos reales, obtener una lista de discos reales, obtener un registro de discos reales y otras funciones.
Debido a la gran extensibilidad de la plataforma FMZ, puede crear su propia plataforma cuantitativa en función de la API de extensión, permitir que los usuarios ejecuten discos en su plataforma, etc. Referencias específicas: https://www.fmz.com/bbs-topic/1697
El mercado de comercio de moneda digital es cada vez más el foco de atención de los comerciantes de la cuantificación debido a su particularidad, de hecho, la negociación programada ya es la corriente principal de la moneda digital, y las estrategias como el mercado de cobertura están activas en el mercado todo el tiempo. Los principiantes con una base de programación débil que quieren entrar en este campo, enfrentan numerosos intercambios y múltiples APIs, y las dificultades se superponen. La plataforma de cuantificación de los inventores (FMZ) (BotVs, www.fmz.com) es la plataforma de cuantificación de la comunidad de moneda digital más grande en la actualidad, y ha ayudado a miles de principiantes a emprender el camino hacia el comercio de la cuantificación durante más de 4 años.
DifusiónCurso de comercio cuantitativo de monedas digitales en las clases de NetEase Cloud│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
El consumidor hace clic en el enlace de promoción, y se registra dentro de los seis meses de la recarga, nuestra división de reembolso de la comisión de acuerdo con la cantidad válida en el pedido válido. La comisión se devolverá en forma de puntos a la cuenta de los promotores, los usuarios pueden cambiar el inventor cuantificar el saldo de la cuenta de la plataforma de negociación en una proporción de 10: 1, también se puede utilizar el punto para cambiar el inventor cuantificar los productos circundantes. Actividad específica enlace: https://www.fmz.com/bbs-topic/3828
El sitio web completo de FMZ se puede desplegar en el servidor exclusivo de la empresa o el equipo, para lograr un control completo y la personalización de la función. El sitio web de FMZ ha sido utilizado y probado por aproximadamente 100 mil usuarios, y ha alcanzado una alta disponibilidad y seguridad, lo que permite ahorrar tiempo a los equipos de cuantificación y a las empresas. La versión empresarial está orientada a equipos de comercio de cuantificación de tamaño medio, proveedores de servicios de futuros de mercancías, etc.
Un sistema profesional para proporcionar liquidez de mercado y administración de fondos para las bolsas de valores, probablemente el mejor sistema de negociación del mercado, utilizado por muchas bolsas y equipos.
El sistema de intercambio de tecnología del inventor utiliza la tecnología de fusión de memoria, la velocidad de procesamiento de pedidos es de hasta 2 millones de pilas / segundo, lo que garantiza que el procesamiento de pedidos no se produzca ningún retraso y cartón. Puede mantener un funcionamiento fluido y estable de las bolsas con más de 20 millones de usuarios en línea al mismo tiempo. La arquitectura del sistema de múltiples capas y múltiples grupos garantiza la seguridad, estabilidad y facilidad de expansión del sistema. La implementación de funciones y actualizaciones de versiones se realiza sin interrupciones, garantizando al máximo la experiencia operativa de los usuarios finales.