Projeto de sistema de gerenciamento de sincronização de pedidos baseado na quantificação FMZ (1)

Criado em: 2022-02-14 19:46:30, atualizado em: 2023-09-15 20:44:11
comments   11
hits   1458

Projeto de sistema de gerenciamento de sincronização de pedidos baseado na quantificação FMZ (1)

Projeto de sistema de gerenciamento de sincronização de pedidos baseado na quantificação FMZ (1)

Em artigos anteriores da Biblioteca FMZ, projetamos diversas estratégias de sincronização de ordens e posições.

Elas servem para colocar a conta de referência e a conta de sincronização em uma estratégia para gerenciar e sincronizar ordens e posições. Hoje, tentaremos um design diferente. Com base na poderosa interface API estendida da plataforma de negociação quantitativa FMZ, projetaremos um sistema de gerenciamento de sincronização de ordens.

Ideias de design

Primeiro, precisamos de algumas boas sugestões e necessidades. As duas estratégias de sincronização de ordem e posição anteriores acima têm vários pontos problemáticos óbvios. Vamos discuti-los juntos:

  • 1. O implementador da estratégia de sincronização deve ter a CHAVE de API de troca da conta de referência e a CHAVE de API de troca da conta de sincronização. O cenário de uso para esta pergunta é: não há problema para suas outras contas de câmbio seguirem uma de suas próprias contas. Mas será problemático se a conta de referência e a conta de sincronização não tiverem o mesmo proprietário. Às vezes, devido a questões de segurança, o proprietário de uma conta sincronizada não está disposto a fornecer a CHAVE API de sua conta de câmbio. Mas como posso fazer pedidos de forma síncrona sem fornecer a CHAVE API?

Solução: Usando a interface de API estendida da FMZ, o proprietário da conta de sincronização (seguidor) só precisa registrar a plataforma de negociação quantitativa FMZ e então executar uma estratégia (no sistema projetado neste artigo:订单同步管理系统(Synchronous Server)Estratégia de mercado real). Em seguida, forneça a CHAVE API estendida da FMZ (observe que não é a CHAVE API da conta de câmbio) e a ID em tempo real do sistema de gerenciamento de sincronização de ordens (Servidor Síncrono) ao proprietário da conta de referência (a pessoa que traz a ordem) . Ao se referir à ordem real do titular da conta (aquele com a ordem) (no sistema projetado neste artigo)订单同步管理系统类库(Single Server)) envia um sinal, a conta real do proprietário da conta sincronizada receberá o sinal de negociação e fará uma ordem automaticamente depois.

    1. Muitos desenvolvedores têm estratégias melhores, mas não podem usar as duas estratégias anteriores de sincronização de ordens e posições descritas acima. Porque isso exigiria integrar a própria estratégia com essas estratégias de sincronização, e a estratégia pode precisar ser revisada, o que seria demorado e trabalhoso. Existe uma boa maneira de atualizar algumas das minhas estratégias maduras diretamente para a função de sincronização de ordens? Solução: Você pode criar uma biblioteca de modelos de sincronização de pedidos (o sistema criado neste artigo订单同步管理系统类库(Single Server)Estratégia), permitindo que o proprietário da conta de referência (a pessoa que recebe o pedido) incorpore diretamente esta biblioteca de modelos em sua própria estratégia para obter a função de sincronização de pedidos e posições.
    1. Reduza um pedido real extra. O último ponto problemático é se você usar as duas estratégias de sincronização de ordens e posições anteriores descritas acima. É necessário abrir uma conta real adicional para monitorar as posições da conta de referência (com uma única conta). Solução: Use a biblioteca de modelos para incorporar funcionalidades em estratégias de contas de referência.

Então esse sistema consiste em 2 partes: 1. Biblioteca de classes do sistema de gerenciamento de sincronização de pedidos (servidor único) 2. Sistema de gerenciamento síncrono de pedidos (servidor síncrono)

Agora que os requisitos estão claros, vamos começar a projetar!

Design 1: Biblioteca de classes do sistema de gerenciamento de sincronização de pedidos (servidor único)

Observe que isso não é uma estratégia. É uma biblioteca de classes de template do FMZ. O conceito de biblioteca de classes de template pode ser pesquisado na documentação da API do FMZ, então não entrarei em detalhes aqui.

Código da biblioteca de modelos:

// 全局变量
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)
    }
}

O design é muito simples, esta biblioteca de classes tem 2 funções funcionais. Quando uma estratégia de negociação programática na plataforma FMZ faz referência订单同步管理系统类库(Single Server)Depois da biblioteca de modelos. Essa estratégia pode ser implementada usando a seguinte função.

  • $.PosMonitor A função é monitorar as mudanças de posição do objeto de câmbio na estratégia e, então, enviar sinais de negociação para o mercado real definido nos parâmetros do modelo: Biblioteca de classes do sistema de gerenciamento de sincronização de ordens (servidor único).

  • $.getTbl Retorna os dados de sincronização monitorados.

O exemplo de uso está em: Modelo de biblioteca de classes do sistema de gerenciamento de sincronização de pedidos (servidor único)mainNa função:

// 引用该模板类库的策略调用范例
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)
    }
}

Uma biblioteca de modelos também pode criar estratégias reais, que geralmente são usadas para testar a biblioteca de modelos. Por exemplo, um teste deste modelo. Você pode entender o modelomainUma função é uma estratégia sua.mainfunção.

O código de teste é escrito para usar o teste de disco de simulação OKEX. É necessário configurar a CHAVE API do disco de simulação OKEX no FMZ como uma conta de referência (com ordens) e começar a alternar para o disco de simulação na função principal. Em seguida, defina o par de negociação como ETH_USDT e o contrato como perpétuo (swap). Em seguida, insira um loop while. Uma ordem é colocada a cada 3 minutos no ciclo para simular o acionamento da negociação de estratégia. O loop while chama$.PosMonitor(0, "ETH_USDT", "swap")O primeiro parâmetro desta função é passado como 0, indicando trocas de monitoramento[0] Este objeto de troca monitora o par de negociação ETH_USDT e o contrato de swap. Então ligue$.getTbl()Para obter informações do gráfico, useLogStatus(_D(), "\n" + "” + JSON.stringify(tbl) + “")Permite que os dados do gráfico sejam exibidos na barra de status.

Então você vê, desde que você o use em uma política que faça referência a este modelo$.PosMonitor(0, "ETH_USDT", "swap"), a estratégia pode ser equipada com a função de monitorar as posições de um determinado produto e enviar mensagens com base nas mudanças de posição.

Antes de testar, explique订单同步管理系统类库(Single Server)Parâmetros de design de estratégia: Acabei de falar sobre como usar a função de interface de modelo para permitir que uma atualização de estratégia tenha uma única função. Então, para quem o sinal é enviado quando a posição muda? A quem esta pergunta é enviada é determinado por订单同步管理系统类库(Single Server)Parâmetros a configurar.

Projeto de sistema de gerenciamento de sincronização de pedidos baseado na quantificação FMZ (1)

Você pode ver que há 5 parâmetros, suportando até 5 pushes (você pode expandi-los se precisar aumentá-los), e o parâmetro padrão é uma string vazia, o que significa que ele não é processado. Formato da string de configuração: label, robotId, accessKey, secretKey

  • label O rótulo da conta sincronizada é usado para marcar uma conta específica, e o nome pode ser definido conforme desejado.

  • robotId ID real, criado pelo proprietário da conta de sincronização订单同步管理系统(Synchronous Server)O ID da transação real.

  • accessKey Acesso estendido à API da FMZKey

  • secretKey API estendida da FMZ secretKey

Em seguida, podemos fazer um teste simples.

Biblioteca de classes do sistema de gerenciamento de sincronização de pedidos (servidor único) operação de disco real:

Projeto de sistema de gerenciamento de sincronização de pedidos baseado na quantificação FMZ (1)

O sistema de gerenciamento de sincronização de pedidos (Synchronous Server) recebeu o sinal: Ainda não concluímos o design do sistema de gerenciamento de sincronização de pedidos (Synchronous Server). Vamos primeiro implementá-lo com um código simples que não faz transações, mas apenas imprime sinais:

Código temporário do sistema de gerenciamento de sincronização de pedidos (Synchronous Server):

function main() {
    LogReset(1)
    while (true) {
        var cmd = GetCommand()
        if (cmd) {
            // cmd: ETH_USDT,swap,buy,1
            Log("cmd: ", cmd)
        }
        Sleep(1000)
    }
}

Projeto de sistema de gerenciamento de sincronização de pedidos baseado na quantificação FMZ (1)

Você pode ver que a conta real do proprietário da conta de sincronização recebeu as informações:ETH_USDT,swap,buy,1。 Dessa forma, o próximo passo é seguir automaticamente a ordem com base no par de negociação, código do contrato, direção de negociação e quantidade nas informações.

Atualmente订单同步管理系统(Synchronous Server)Este é apenas um código temporário. Continuaremos a explorar seu design na próxima edição.