avatar of 发明者量化-小小梦 发明者量化-小小梦
Seguir Mensajes Privados
4
Seguir
1271
Seguidores

Diseño de un sistema de gestión de sincronización de órdenes basado en cuantificación FMZ (1)

Creado el: 2022-02-14 19:46:30, Actualizado el: 2025-05-16 16:36:53
comments   11
hits   1940

Diseño de un sistema de gestión de sincronización de órdenes basado en cuantificación FMZ (1)

Diseño de un sistema de gestión de sincronización de órdenes basado en cuantificación FMZ (1)

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.

Ideas de diseño

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:

  • 1. El implementador de la estrategia de sincronización debe tener la CLAVE API de intercambio de la cuenta de referencia y la CLAVE API de intercambio de la cuenta de sincronización. El escenario de uso para esta pregunta es: no hay problema para sus otras cuentas de exchange seguir una de sus propias cuentas. Pero será problemático si la cuenta de referencia y la cuenta de sincronización no tienen el mismo propietario. A veces, debido a problemas de seguridad, el propietario de una cuenta sincronizada no está dispuesto a proporcionar la CLAVE API de su cuenta de exchange. Pero ¿cómo puedo realizar pedidos de forma sincrónica sin proporcionar API KEY?

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.

    1. Muchos desarrolladores tienen mejores estrategias, pero no pueden utilizar las dos estrategias de sincronización de posición y orden pasado descritas anteriormente. Porque eso requeriría integrar la propia estrategia con estas estrategias de sincronización, y podría ser necesario revisar la estrategia, lo que sería una tarea laboriosa y que consumiría mucho tiempo. ¿Existe una buena manera de actualizar algunas de mis estrategias maduras directamente a la función de sincronización de órdenes? Solución: Puede diseñar una biblioteca de plantillas de sincronización de pedidos (el sistema diseñado en este artículo订单同步管理系统类库(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.
    1. Reducir un pedido real extra. El último punto problemático es si utiliza las dos estrategias de sincronización de posición y orden pasadas descritas anteriormente. Es necesario abrir una cuenta real adicional para monitorear las posiciones de la cuenta de referencia (con una sola cuenta). Solución: Utilice la biblioteca de plantillas para integrar funcionalidad en las estrategias de cuentas de referencia.

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!

Diseño 1: Biblioteca de clases del sistema de gestión de sincronización de pedidos (servidor único)

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.

Diseño de un sistema de gestión de sincronización de órdenes basado en cuantificación FMZ (1)

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

Diseño de un sistema de gestión de sincronización de órdenes basado en cuantificación FMZ (1)

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

Diseño de un sistema de gestión de sincronización de órdenes basado en cuantificación FMZ (1)

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.