Thiết kế hệ thống quản lý đồng bộ dựa trên lệnh FMZ (1)

Tác giả:Ninabadass, Tạo: 2022-04-06 15:08:26, Cập nhật: 2022-04-24 18:00:51

Thiết kế hệ thống quản lý đồng bộ dựa trên lệnh FMZ (1)

Trong các bài báo trước trên FMZ Digest, chúng tôi đã thiết kế một số thứ tự và vị trí chiến lược đồng bộ.

Các thiết kế này đưa tài khoản tham chiếu và tài khoản đồng bộ vào cùng một chiến lược để được quản lý để nhận ra việc đồng bộ hóa các đơn đặt hàng và vị trí.

Suy nghĩ về thiết kế

Trước hết, chúng ta cần một số gợi ý và yêu cầu tốt. Hai chiến lược đồng bộ hóa lệnh và vị trí trước đây có một số nhược điểm rõ ràng.

  • 1.Người dùng thực hiện đồng bộ hóa bot chiến lược phải có API KEY của nền tảng tài khoản tham chiếu và API KEY của tài khoản đồng bộ hóa. Đối với kịch bản sử dụng, vấn đề là: tài khoản nền tảng khác của bạn có thể theo dõi một trong các tài khoản của riêng bạn. Nhưng nó có thể gây khó khăn cho kịch bản khi tài khoản tham chiếu và tài khoản đồng bộ không có chủ sở hữu tương tự. Chủ sở hữu tài khoản đồng bộ đôi khi không muốn cung cấp API KEY của tài khoản nền tảng của mình do các cân nhắc an ninh. Nhưng làm thế nào để đặt lệnh giao dịch đồng bộ mà không cung cấp API KEY?

    Giải pháp: Sử dụng API mở rộng FMZ, chủ sở hữu tài khoản đồng bộ hóa (người giám sát lệnh) chỉ cần đăng ký nền tảng giao dịch FMZ Quant, và sau đó chạy một chiến lược (trong hệ thống được thiết kế trong bài viết này:Order Synchronous Management System (Synchronous Server)Sau đó, API KEY mở rộng của FMZ (lưu ý rằng nó không phải là API KEY của tài khoản nền tảng) và ID bot của hệ thống quản lý đồng bộ hóa đơn hàng (Synchronous Server) sẽ được cung cấp cho chủ sở hữu tài khoản tham chiếu (sở hữu đơn hàng). Khi bot của chủ tài khoản tham chiếu (người chủ lệnh) (Order Synchronous Management System Library (Single Server)trong hệ thống được thiết kế trong bài viết) gửi tín hiệu, bot chủ tài khoản đồng bộ hóa sẽ nhận tín hiệu giao dịch.

  • 2. Nhiều nhà phát triển có các chiến lược tốt hơn và không thể sử dụng hai chiến lược đồng bộ hóa thứ tự và vị trí trước đây được mô tả ở trên. Bởi vì điều đó cần phải hợp nhất các chiến lược của riêng họ với các chiến lược đồng bộ hóa này, và các chiến lược của họ có thể cần phải được sửa đổi rất nhiều, tốn nhiều thời gian và lao động. Có một cách tốt để trực tiếp nâng cấp một số chiến lược trưởng thành của bạn sang các chiến lược có chức năng đồng bộ hóa thứ tự không?

    Giải pháp: Bạn có thể thiết kế một thư viện mẫu đồng bộ để (theOrder Synchronous Management System Library (Single Server)chiến lược trong hệ thống được thiết kế trong bài viết), để chủ sở hữu tài khoản tham chiếu (người chủ lệnh) có thể trực tiếp chèn thư viện mẫu này vào chiến lược của riêng mình để đạt được đồng bộ hóa thứ tự và vị trí.

  • 3.Giảm thêm một bot Nhược điểm cuối cùng là nếu bạn sử dụng hai lệnh trước đây và chiến lược đồng bộ hóa vị trí được mô tả ở trên, cần phải mở một vị trí bổ sung của tài khoản tham chiếu (tài khoản có lệnh) để theo dõi bot. Giải pháp: Sử dụng thư viện mẫu để nhúng hàm vào chiến lược tài khoản tham chiếu.

Do đó, hệ thống bao gồm hai phần: 1.order thư viện hệ thống quản lý đồng bộ (Single Server) Hệ thống quản lý đồng bộ đơn đặt hàng (Synchronous Server)

Một khi chúng ta chắc chắn yêu cầu của chúng ta, hãy bắt đầu thiết kế!

Thiết kế 1: Đặt thư viện hệ thống quản lý đồng bộ (Máy chủ duy nhất)

Chú ý rằng ở đây nó không phải là một chiến lược, nhưng một thư viện mẫu FMZ, có thể được tìm kiếm trong FMZ API tài liệu và chúng tôi sẽ không thảo luận ở đây.

Mã mẫu:

// Global variable
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) {
        // Open position
        var tradeDirection = type == PD_LONG ? "buy" : "sell"
        // Send signal
        msg = symbol + "," + ct + "," + tradeDirection + "," + Math.abs(delta)        
    } else if (delta < 0) {
        // Open position 
        var tradeDirection = type == PD_LONG ? "closebuy" : "closesell"
        if (nowAmount <= 0) {
            Log("No position detected")
            return 
        }
        // Send signal 
        msg = symbol + "," + ct + "," + tradeDirection + "," + Math.abs(delta)
    } else {
        throw "error"
    }
    if (msg) {
        _.each(fmzExtendApis, function(extendApiConfig) {
            var ret = sendCommandRobotMsg(extendApiConfig[keyName_robotId], extendApiConfig[keyName_extendAccessKey], extendApiConfig[keyName_extendSecretKey], msg)
            Log("Call CommandRobot interface,", "label:", extendApiConfig[keyName_label], ", msg:", msg, ", ret:", ret)
            Sleep(1000)
        })
    }
}

$.PosMonitor = function(exIndex, symbol, ct) {    
    var ts = new Date().getTime()
    var ex = exchanges[exIndex]
    // Judge the type of ex
    var exName = ex.GetName()
    var isFutures = exName.includes("Futures_")
    var exType = isFutures ? "futures" : "spot"
    if (!isFutures) {
        throw "Only support futures order supervising"
    }

    if (exType == "futures") {
        // Cache symbol ct
        var buffSymbol = ex.GetCurrency()
        var buffCt = ex.GetContractType()

        // Switch to the corresponding trading pair and contract code 
        ex.SetCurrency(symbol)
        if (!ex.SetContractType(ct)) {
            throw "SetContractType failed"
        }

        // Monitor position 
        var keyInitRefPosAmount = "refPos-" + exIndex + "-" + symbol + "-" + ct    // refPos-exIndex-symbol-contractType
        var initRefPosAmount = mapInitRefPosAmount[keyInitRefPosAmount]
        if (!initRefPosAmount) {
            // The data is not initialized; initialize it          
            mapInitRefPosAmount[keyInitRefPosAmount] = getPosAmount(_C(ex.GetPosition), ct)
            initRefPosAmount = mapInitRefPosAmount[keyInitRefPosAmount]
        }

        // Monitor
        var nowRefPosAmount = getPosAmount(_C(ex.GetPosition), ct)
        // Calculate position changes 
        var longPosDelta = nowRefPosAmount.long - initRefPosAmount.long
        var shortPosDelta = nowRefPosAmount.short - initRefPosAmount.short

        // Detect changes 
        if (!(longPosDelta == 0 && shortPosDelta == 0)) {
            // Execute long position action 
            if (longPosDelta != 0) {
                Log(ex.GetName(), ex.GetLabel(), symbol, ct, "Execute long position order supervision, change volume:", longPosDelta)
                follow(nowRefPosAmount, symbol, ct, PD_LONG, longPosDelta)
            }
            // Execute short position action 
            if (shortPosDelta != 0) {
                Log(ex.GetName(), ex.GetLabel(), symbol, ct, "Execute short position order supervision, change volume:", shortPosDelta)
                follow(nowRefPosAmount, symbol, ct, PD_SHORT, shortPosDelta)
            }

            // After executing the order supervision operation, update  
            mapInitRefPosAmount[keyInitRefPosAmount] = nowRefPosAmount
        }

        // Recover symbol ct
        ex.SetCurrency(buffSymbol)
        ex.SetContractType(buffCt)
    } else if (exType == "spot") {
        // Spot 
    }
}

$.getTbl = function() {
    var tbl = {
        "type" : "table", 
        "title" : "Synchrodata", 
        "cols" : [], 
        "rows" : []
    }
    // Construct the table title 
    tbl.cols.push("Monitoring account:refPos-exIndex-symbol-contractType")
    tbl.cols.push(`Mintoring position:{"timestamp":xxx,"long position volume":xxx,"short position volume":xxx}`)
    _.each(fmzExtendApis, function(extendApiData, index) {
        tbl.cols.push(keyName_robotId + "-" + index)
    })
    
    // Write in the data 
    _.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
}

// Invocation example of the strategy in the template
function main() {
    // Clear all logs
    LogReset(1)

    //Switch to OKEX simulated bot test
    exchanges[0].IO("simulate", true)

    // Set contract 
    exchanges[0].SetCurrency("ETH_USDT")
    exchanges[0].SetContractType("swap")

    // Timed trading time interval
    var tradeInterval = 1000 * 60 * 3        // trade every three minutes, to observe the order supervising signal  
    var lastTradeTS = new Date().getTime()
    
    while (true) {
        // Other logic of the strategy...

        // Used to test the simulated trade trigger  
        var ts = new Date().getTime()
        if (ts - lastTradeTS > tradeInterval) {
            Log("Simulated strategy with orders has trades, and positions changed", "#FF0000")
            exchanges[0].SetDirection("buy")
            exchanges[0].Buy(-1, 1)
            lastTradeTS = ts
        }

        // Call the interface function in the template 
        $.PosMonitor(0, "ETH_USDT", "swap")    // You can set multiple monitors, to minitor different exchange objects in the strategy with orders
        var tbl = $.getTbl()
        
        // Display the status bar 
        LogStatus(_D(), "\n" + "`" + JSON.stringify(tbl) + "`")
        Sleep(1000)
    }
}

Thiết kế rất đơn giản, thư viện có 2 chức năng.Order Synchronous Management System Library (Single Server)thư viện lớp mẫu. Chiến lược này sau đó có thể sử dụng các hàm sau.

  • $. PosMonitor Hiệu ứng của chức năng này là theo dõi sự thay đổi vị trí của các đối tượng trao đổi trong chiến lược, và sau đó gửi tín hiệu giao dịch đến bot được đặt trong các tham số của mẫu: order synchronous management system library (Single Server).
  • $.getTbl Chức năng trả về dữ liệu đồng bộ được theo dõi.

Ví dụ sử dụng là tạimainchức năng trong thư viện hệ thống quản lý đồng bộ đơn hàng (Single Server):

// Invocation example of the strategy in the template 
function main() {
    // Clear all logs 
    LogReset(1)

    // Switch to OKEX simulated bot test 
    exchanges[0].IO("simulate", true)

    // Set contract 
    exchanges[0].SetCurrency("ETH_USDT")
    exchanges[0].SetContractType("swap")

    // Timed trading time interval
    var tradeInterval = 1000 * 60 * 3        // trade every three minutes, to observe the order supervising signal  
    var lastTradeTS = new Date().getTime()
    
    while (true) {
        // Other logic of the strateg...

        // Used to test the simulated trade trigger  
        var ts = new Date().getTime()
        var ts = new Date().getTime()
        if (ts - lastTradeTS > tradeInterval) {
            Log("Simulated strategy with orders has trades, and positions changed", "#FF0000")
            exchanges[0].SetDirection("buy")
            exchanges[0].Buy(-1, 1)
            lastTradeTS = ts
        }

        // Use the interface function of the template 
        $.PosMonitor(0, "ETH_USDT", "swap")    // You can set multiple monitors to monitor different exchange objects on an strategy with orders
        var tbl = $.getTbl()
        
        // Display the status bar
        LogStatus(_D(), "\n" + "`" + JSON.stringify(tbl) + "`")
        Sleep(1000)
    }
}

Một thư viện mẫu tự nó cũng có thể tạo ra một bot chiến lược, thường được sử dụng để kiểm tra thư viện mẫu. Ví dụ: một bài kiểm tra cho mẫu này. Bạn có thể hiểu rằngmainchức năng trong mẫu làmainchức năng của một trong những chiến lược của riêng bạn.

Mã thử nghiệm được viết để sử dụng OKEX simulate bot để thử nghiệm, và API KEY của OKEX simulate bot cần được cấu hình trên FMZ như một tài khoản tham khảo (với lệnh), và chức năng chính bắt đầu chuyển sang bot được mô phỏng. Sau đó đặt cặp giao dịch thành ETH_USDT, và đặt hợp đồng để trao đổi. Sau đó nhập một vòng lặp while. Trong vòng lặp, một lệnh được đặt mỗi 3 phút để mô phỏng kích hoạt các giao dịch chiến lược.$.PosMonitor(0, "ETH_USDT", "swap")được gọi trong vòng lặp while, và tham số đầu tiên của hàm được gọi là 0, có nghĩa là để theo dõi các giao dịch đối tượng trao đổi[0], cặp giao dịch ETH_USDT, và hợp đồng trao đổi.$.getTbl()để có được thông tin biểu đồ, và sử dụngLogStatus(_D(), "\n" + "`" + JSON.stringify(tbl) + "`")để làm cho dữ liệu biểu đồ hiển thị trên thanh trạng thái.

Vì vậy, bạn thấy, miễn là$.PosMonitor(0, "ETH_USDT", "swap")được sử dụng trong một chiến lược gọi mẫu, chiến lược có thể có chức năng giám sát vị trí biểu tượng nhất định và đẩy thông báo thay đổi vị trí.

Trước khi thử nghiệm, giải thích thiết kế tham số củaOrder Synchronous Management System Library (Single Server)chiến lược: Tôi vừa nói về cách sử dụng chức năng giao diện của mẫu để nâng cấp chiến lược với chức năng thực hiện lệnh. Câu hỏi gửi cho ai được cấu hình bởi các tham số củaorder synchronous management system library (Single Server).

img

Bạn có thể thấy năm tham số, có thể hỗ trợ tối đa năm push (nếu bạn cần tăng số push, bạn có thể tự mở rộng nó); mặc định của các tham số là một chuỗi trống, cụ thể là không được xử lý.

  • nhãn Nhãn của tài khoản đồng bộ, được sử dụng để gắn nhãn tài khoản; tên nhãn có thể được đặt ngẫu nhiên.

  • robotId ID bot củaorder synchronous management system (Synchronous Server)được tạo bởi chủ tài khoản đồng bộ.

  • accessKey AccessKey của FMZ mở rộng API.

  • mậtKey Chìa khóa bí mật của FMZ mở rộng API.

Sau đó, chúng ta có thể thực hiện một bài kiểm tra đơn giản.

Đặt lệnh thư viện hệ thống quản lý đồng bộ (Single Server) hoạt động bot:

img

Order Synchronous Management System (Synchronous Server) bot nhận được tín hiệu: Order Synchronous Management System (Synchronous Server) bây giờ chưa được thiết kế hoàn toàn bởi chúng tôi, và chúng tôi có thể sử dụng một mã đơn giản để thực hiện nó, mà không cần giao dịch, chỉ in tín hiệu:

Order Synchronous Management System (Synchronous Server) mã tạm thời:

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

img

Như bạn có thể thấy, bot của chủ tài khoản đồng bộ đã nhận được tin nhắn:ETH_USDT,swap,buy,1. Vì vậy, trong bước tiếp theo, chúng tôi có thể giám sát lệnh tự động, theo cặp giao dịch, mã hợp đồng, hướng giao dịch và khối lượng.

Hiện tại,order synchronous management system (Synchronous Server)chỉ là mã tạm thời, và chúng ta có thể thảo luận thêm về thiết kế của nó trong bài viết tiếp theo.


Thêm nữa