
En artículos anteriores de FMZ Library, hemos diseñado varias estrategias de sincronización de orden y posición.
Se trata de poner la cuenta de referencia y la cuenta de sincronización en una sola estrategia para gestionar y sincronizar órdenes y posiciones. Hoy probaremos un diseño diferente. Basándonos en la potente interfaz API extendida de la plataforma de operaciones cuantitativas FMZ, diseñaremos un sistema de gestión de sincronización de órdenes.
Lo primero que necesitamos son algunas buenas sugerencias y necesidades. Las dos estrategias de sincronización de órdenes y posiciones anteriores tienen varios puntos débiles obvios. Analicémoslos juntos:
Solución:
Utilizando la interfaz API extendida de FMZ, el propietario de la cuenta de sincronización (seguidor) solo necesita registrar la plataforma de comercio cuantitativo FMZ y luego ejecutar una estrategia (en el sistema diseñado en este artículo:订单同步管理系统(Synchronous Server)Estrategia mercado real). A continuación, proporcione la CLAVE API extendida de FMZ (nota, no la CLAVE API de la cuenta de intercambio) y el ID en tiempo real del sistema de gestión de sincronización de pedidos (Servidor Síncrono) al propietario de la cuenta de referencia (la persona que trae el pedido) .
Al referirse al pedido real del titular de la cuenta (el que tiene el pedido) (en el sistema diseñado en este artículo)订单同步管理系统类库(Single Server)) envía una señal, la cuenta real del propietario de la cuenta sincronizada recibirá la señal comercial y realizará un pedido automáticamente después.
订单同步管理系统类库(Single Server)Estrategia), que permite al propietario de la cuenta de referencia (la persona que toma la orden) integrar directamente esta biblioteca de plantillas en su propia estrategia para lograr la función de sincronización de órdenes y posiciones.Entonces este sistema consta de dos partes: 1. Biblioteca de clases del sistema de gestión de sincronización de pedidos (servidor único) 2. Ordenar Sistema de Gestión Sincrónica (Servidor Sincrónico)
Ahora que los requisitos están claros, ¡comencemos a diseñar!
Tenga en cuenta que esto no es una estrategia. Es una biblioteca de clases de plantillas de FMZ. El concepto de biblioteca de clases de plantillas se puede buscar en la documentación de la API de FMZ, por lo que no entraré en detalles aquí.
Código de la biblioteca de plantillas:
// 全局变量
var keyName_label = "label"
var keyName_robotId = "robotId"
var keyName_extendAccessKey = "extendAccessKey"
var keyName_extendSecretKey = "extendSecretKey"
var fmzExtendApis = parseConfigs([config1, config2, config3, config4, config5])
var mapInitRefPosAmount = {}
function parseConfigs(configs) {
var arr = []
_.each(configs, function(config) {
if (config == "") {
return
}
var strArr = config.split(",")
if (strArr.length != 4) {
throw "configs error!"
}
var obj = {}
obj[keyName_label] = strArr[0]
obj[keyName_robotId] = strArr[1]
obj[keyName_extendAccessKey] = strArr[2]
obj[keyName_extendSecretKey] = strArr[3]
arr.push(obj)
})
return arr
}
function getPosAmount(pos, ct) {
var longPosAmount = 0
var shortPosAmount = 0
_.each(pos, function(ele) {
if (ele.ContractType == ct && ele.Type == PD_LONG) {
longPosAmount = ele.Amount
} else if (ele.ContractType == ct && ele.Type == PD_SHORT) {
shortPosAmount = ele.Amount
}
})
var timestamp = new Date().getTime()
return {ts: timestamp, long: longPosAmount, short: shortPosAmount}
}
function sendCommandRobotMsg (robotId, accessKey, secretKey, msg) {
// https://www.fmz.com/api/v1?access_key=xxx&secret_key=yyyy&method=CommandRobot&args=[186515,"ok12345"]
var url = "https://www.fmz.com/api/v1?access_key=" + accessKey + "&secret_key=" + secretKey + "&method=CommandRobot&args=[" + robotId + ',"' + msg + '"]'
Log(url)
var ret = HttpQuery(url)
return ret
}
function follow(nowPosAmount, symbol, ct, type, delta) {
var msg = ""
var nowAmount = type == PD_LONG ? nowPosAmount.long : nowPosAmount.short
if (delta > 0) {
// 开仓
var tradeDirection = type == PD_LONG ? "buy" : "sell"
// 发送信号
msg = symbol + "," + ct + "," + tradeDirection + "," + Math.abs(delta)
} else if (delta < 0) {
// 平仓
var tradeDirection = type == PD_LONG ? "closebuy" : "closesell"
if (nowAmount <= 0) {
Log("未检测到持仓")
return
}
// 发送信号
msg = symbol + "," + ct + "," + tradeDirection + "," + Math.abs(delta)
} else {
throw "错误"
}
if (msg) {
_.each(fmzExtendApis, function(extendApiConfig) {
var ret = sendCommandRobotMsg(extendApiConfig[keyName_robotId], extendApiConfig[keyName_extendAccessKey], extendApiConfig[keyName_extendSecretKey], msg)
Log("调用CommandRobot接口,", "label:", extendApiConfig[keyName_label], ", msg:", msg, ", ret:", ret)
Sleep(1000)
})
}
}
$.PosMonitor = function(exIndex, symbol, ct) {
var ts = new Date().getTime()
var ex = exchanges[exIndex]
// 判断ex类型
var exName = ex.GetName()
var isFutures = exName.includes("Futures_")
var exType = isFutures ? "futures" : "spot"
if (!isFutures) {
throw "仅支持期货跟单"
}
if (exType == "futures") {
// 缓存 symbol ct
var buffSymbol = ex.GetCurrency()
var buffCt = ex.GetContractType()
// 切换到对应的交易对、合约代码
ex.SetCurrency(symbol)
if (!ex.SetContractType(ct)) {
throw "SetContractType failed"
}
// 监控持仓
var keyInitRefPosAmount = "refPos-" + exIndex + "-" + symbol + "-" + ct // refPos-exIndex-symbol-contractType
var initRefPosAmount = mapInitRefPosAmount[keyInitRefPosAmount]
if (!initRefPosAmount) {
// 没有初始化数据,初始化
mapInitRefPosAmount[keyInitRefPosAmount] = getPosAmount(_C(ex.GetPosition), ct)
initRefPosAmount = mapInitRefPosAmount[keyInitRefPosAmount]
}
// 监控
var nowRefPosAmount = getPosAmount(_C(ex.GetPosition), ct)
// 计算仓位变动
var longPosDelta = nowRefPosAmount.long - initRefPosAmount.long
var shortPosDelta = nowRefPosAmount.short - initRefPosAmount.short
// 检测变动
if (!(longPosDelta == 0 && shortPosDelta == 0)) {
// 执行多头动作
if (longPosDelta != 0) {
Log(ex.GetName(), ex.GetLabel(), symbol, ct, "执行多头跟单,变动量:", longPosDelta)
follow(nowRefPosAmount, symbol, ct, PD_LONG, longPosDelta)
}
// 执行空头动作
if (shortPosDelta != 0) {
Log(ex.GetName(), ex.GetLabel(), symbol, ct, "执行空头跟单,变动量:", shortPosDelta)
follow(nowRefPosAmount, symbol, ct, PD_SHORT, shortPosDelta)
}
// 执行跟单操作后,更新
mapInitRefPosAmount[keyInitRefPosAmount] = nowRefPosAmount
}
// 恢复 symbol ct
ex.SetCurrency(buffSymbol)
ex.SetContractType(buffCt)
} else if (exType == "spot") {
// 现货
}
}
$.getTbl = function() {
var tbl = {
"type" : "table",
"title" : "同步数据",
"cols" : [],
"rows" : []
}
// 构造表头
tbl.cols.push("监控账户:refPos-exIndex-symbol-contractType")
tbl.cols.push(`监控持仓:{"时间戳":xxx,"多头持仓量":xxx,"空头持仓量":xxx}`)
_.each(fmzExtendApis, function(extendApiData, index) {
tbl.cols.push(keyName_robotId + "-" + index)
})
// 写入数据
_.each(mapInitRefPosAmount, function(initRefPosAmount, key) {
var arr = [key, JSON.stringify(initRefPosAmount)]
_.each(fmzExtendApis, function(extendApiData) {
arr.push(extendApiData[keyName_robotId])
})
tbl.rows.push(arr)
})
return tbl
}
// 引用该模板类库的策略调用范例
function main() {
// 清除所有日志
LogReset(1)
// 切换到OKEX 模拟盘测试
exchanges[0].IO("simulate", true)
// 设置合约
exchanges[0].SetCurrency("ETH_USDT")
exchanges[0].SetContractType("swap")
// 定时交易时间间隔
var tradeInterval = 1000 * 60 * 3 // 三分钟交易一次,用于观察跟单信号
var lastTradeTS = new Date().getTime()
while (true) {
// 策略其它逻辑...
// 用于测试的模拟交易触发
var ts = new Date().getTime()
if (ts - lastTradeTS > tradeInterval) {
Log("模拟带单策略发生交易,持仓变化", "#FF0000")
exchanges[0].SetDirection("buy")
exchanges[0].Buy(-1, 1)
lastTradeTS = ts
}
// 使用模板的接口函数
$.PosMonitor(0, "ETH_USDT", "swap") // 可以设置多个监控,监控带单策略上的不同的exchange对象
var tbl = $.getTbl()
// 显示状态栏
LogStatus(_D(), "\n" + "`" + JSON.stringify(tbl) + "`")
Sleep(1000)
}
}
El diseño es muy simple, esta biblioteca de clases tiene 2 funciones funcionales. Cuando una estrategia de trading programático en la plataforma FMZ hace referencia订单同步管理系统类库(Single Server)Después de la biblioteca de plantillas. Esta estrategia se puede implementar utilizando la siguiente función.
$.PosMonitor La función es monitorear los cambios de posición del objeto de intercambio en la estrategia, y luego enviar señales comerciales al mercado real establecido en los parámetros de la plantilla: Biblioteca de clases del sistema de gestión de sincronización de órdenes (servidor único).
$.getTbl Devuelve los datos de sincronización monitoreados.
El ejemplo de uso se encuentra en: Plantilla de biblioteca de clases del sistema de gestión de sincronización de pedidos (servidor único)mainEn la función:
// 引用该模板类库的策略调用范例
function main() {
// 清除所有日志
LogReset(1)
// 切换到OKEX 模拟盘测试
exchanges[0].IO("simulate", true)
// 设置合约
exchanges[0].SetCurrency("ETH_USDT")
exchanges[0].SetContractType("swap")
// 定时交易时间间隔
var tradeInterval = 1000 * 60 * 3 // 三分钟交易一次,用于观察跟单信号
var lastTradeTS = new Date().getTime()
while (true) {
// 策略其它逻辑...
// 用于测试的模拟交易触发
var ts = new Date().getTime()
if (ts - lastTradeTS > tradeInterval) {
Log("模拟带单策略发生交易,持仓变化", "#FF0000")
exchanges[0].SetDirection("buy")
exchanges[0].Buy(-1, 1)
lastTradeTS = ts
}
// 使用模板的接口函数
$.PosMonitor(0, "ETH_USDT", "swap") // 可以设置多个监控,监控带单策略上的不同的exchange对象
var tbl = $.getTbl()
// 显示状态栏
LogStatus(_D(), "\n" + "`" + JSON.stringify(tbl) + "`")
Sleep(1000)
}
}
Una biblioteca de plantillas también puede crear estrategias reales, que normalmente se utilizan para probar la biblioteca de plantillas. Por ejemplo, una prueba de esta plantilla. Puedes entender la plantillamainUna función es una estrategia propia.mainfunción.
El código de prueba está escrito para utilizar la prueba del disco de simulación de OKEX. Es necesario configurar la CLAVE API del disco de simulación de OKEX en FMZ como una cuenta de referencia (con pedidos) y comenzar a cambiar al disco de simulación en la función principal. Luego, configure el par comercial en ETH_USDT y el contrato en perpetuo (swap). A continuación, introduzca un bucle while. Se coloca una orden cada 3 minutos en el ciclo para simular la activación de la estrategia comercial. El bucle while llama$.PosMonitor(0, "ETH_USDT", "swap")El primer parámetro de esta función se pasa como 0, lo que indica intercambios de monitoreo.[0] Este objeto de intercambio monitorea el par comercial ETH_USDT y el contrato de swap. Entonces llama$.getTbl()Para obtener información del gráfico, utiliceLogStatus(_D(), "\n" + "” + JSON.stringify(tbl) + “")Permite que los datos del gráfico se muestren en la barra de estado.
Entonces, ya ves, siempre que lo uses en una política que haga referencia a esta plantilla$.PosMonitor(0, "ETH_USDT", "swap")La estrategia puede equiparse con la función de monitorear las posiciones de un determinado producto y enviar mensajes basados en cambios de posición.
Antes de realizar la prueba, por favor explique订单同步管理系统类库(Single Server)Diseño de parámetros de estrategia:
Acabo de hablar sobre cómo utilizar la función de interfaz de plantilla para permitir que una actualización de estrategia tenga una única función. Entonces ¿a quién se envía la señal cuando la posición cambia?
A quién se envía esta pregunta lo determina订单同步管理系统类库(Single Server)Parámetros a configurar.

Puedes ver que hay 5 parámetros, que admiten hasta 5 push (puedes expandirlo si necesitas aumentarlo), y el parámetro tiene como valor predeterminado una cadena vacía, lo que significa que no se procesa. Formato de cadena de configuración: etiqueta, robotId, accessKey, secretKey
label La etiqueta de la cuenta sincronizada se utiliza para marcar una cuenta determinada y el nombre se puede configurar a voluntad.
robotId
ID real, creado por el propietario de la cuenta de sincronización订单同步管理系统(Synchronous Server)El ID de la transacción real.
accessKey Acceso a la API extendida de FMZ
secretKey SecretKey de la API extendida de FMZ
A continuación podemos hacer una prueba sencilla.
Operación de disco real de la biblioteca de clases del sistema de gestión de sincronización de órdenes (servidor único):

El sistema de gestión de sincronización de pedidos (Servidor Sincrónico) recibió la señal: Aún no hemos terminado el diseño del sistema de gestión de sincronización de órdenes (Synchronous Server). Primero vamos a implementarlo con un código simple que no realiza transacciones sino que solo imprime señales:
Código temporal del sistema de gestión de sincronización de pedidos (Servidor Síncrono):
function main() {
LogReset(1)
while (true) {
var cmd = GetCommand()
if (cmd) {
// cmd: ETH_USDT,swap,buy,1
Log("cmd: ", cmd)
}
Sleep(1000)
}
}

Puedes ver que la cuenta real del propietario de la cuenta de sincronización ha recibido la información:ETH_USDT,swap,buy,1。
De esta manera, el siguiente paso es seguir automáticamente la orden en función del par comercial, el código del contrato, la dirección comercial y la cantidad en la información.
Actualmente订单同步管理系统(Synchronous Server)Este es solo un código temporal, continuaremos explorando su diseño en el próximo número.