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

Tác giả:Lydia., Tạo: 2022-11-07 10:20:01, Cập nhật: 2023-09-15 20:45:23

img

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

Trong các bài viết trước đây trong thư viện FMZs, chúng tôi đã thiết kế một số loại chiến lược đồng bộ hóa cho thứ tự và vị trí.

Chúng quản lý tài khoản tham chiếu và tài khoản đồng bộ trong một chiến lược để đạt được đồng bộ hóa lệnh và vị trí. và hôm nay chúng tôi sẽ thử một thiết kế khác, chúng tôi sẽ thiết kế một hệ thống quản lý đồng bộ hóa lệnh dựa trên giao diện API mở rộng mạnh mẽ của nền tảng giao dịch FMZ Quant.

Ý tưởng thiết kế

Đầu tiên, chúng ta cần một số đề xuất tốt và nhu cầu. hai lệnh trước đây và vị trí chiến lược đồng bộ hóa trên, mà có một số thiếu sót rõ ràng, mà chúng tôi sẽ thảo luận cùng nhau.

    1. Người dùng của các chiến lược đồng bộ hóa thực bot phải có trao đổi API KEY của tài khoản tham chiếu, và trao đổi API KEY của tài khoản đồng bộ hóa. Vấn đề này là tốt cho tình huống sử dụng khi các tài khoản trao đổi khác theo dõi tài khoản của riêng mình. Tuy nhiên, nó có thể gây khó khăn cho tình huống khi tài khoản tham chiếu và tài khoản đồng bộ không phải là chủ sở hữu tương tự. Đôi khi chủ sở hữu tài khoản đồng bộ không muốn cung cấp API KEY của tài khoản trao đổi của riêng mình vì lý do bảo mật, nhưng làm thế nào để đồng bộ hóa các giao dịch lệnh mà không cung cấp API KEY?

    Giải pháp: Sử dụng giao diện API mở rộng của FMZ, chủ sở hữu tài khoản đồng bộ (người theo dõi lệnh) chỉ cần đăng ký tài khoản trên Nền tảng giao dịch FMZ Quant, 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 ServerChiến lược trong Real Bot). Sau đó chỉ cần cung cấp FMZ mở rộng API KEY (lưu ý rằng nó không phải là API KEY của tài khoản trao đổi) và Order Synchronous Server thực bot ID cho chủ sở hữu của tài khoản tham chiếu (đạo diễn lệnh). Khi chủ tài khoản tham chiếu (những người theo dõi lệnh) thực bot (cácOrder Synchronization Management System Class Library (Single Server)trong hệ thống được thiết kế trong bài viết này) gửi tín hiệu, chủ tài khoản đồng bộ hóa bot thực sự sẽ nhận tín hiệu giao dịch và đặt lệnh tiếp theo tự động.

    1. Nhiều nhà phát triển có các chiến lược tốt, nhưng họ không thể sử dụng 2 chiến lược đồng bộ hóa thứ tự và vị trí đã được mô tả ở trên. Bởi vì họ cần tích hợp 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ó thể cần phải thay đổi đáng kể, sẽ tốn rất nhiều công việc và nỗ lực. Có cách tốt để nâng cấp một số chiến lược trưởng thành của bạn trực tiếp lên 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 lớp mẫu đồng bộ hóa thứ tự (theOrder Synchronization Management System Class Library (Single Server)chiến lược trong hệ thống được thiết kế trong bài viết này), do đó chủ sở hữu tài khoản tham chiếu (đạo lệnh) có thể nhúng thư viện lớp mẫu này vào chiến lược của riêng mình trực tiếp để đạt được chức năng đồng bộ hóa thứ tự và vị trí.
    1. Giảm thêm một robot thực sự. Nhược điểm cuối cùng là nếu bạn sử dụng 2 lệnh trước đây, chiến lược đồng bộ hóa vị trí được mô tả ở trên. Giải pháp: Sử dụng thư viện lớp mẫu để nhúng chức năng trong chiến lược tài khoản tham chiếu.

Vì vậy, hệ thống bao gồm 2 phần:

  1. Thư viện lớp hệ thống quản lý đồng bộ hóa đơn hàng (Single Server)
  2. Hệ thống quản lý đồng bộ hóa đơn hàng (Synchronous Server)

Một khi chúng ta đã xác định nhu cầu của mình, hãy bắt đầu thiết kế!

Thiết kế 1: thư viện lớp hệ thống quản lý đồng bộ hóa đơn hàng (Single Server)

Lưu ý rằng đây không phải là một chiến lược. Đây là một thư viện lớp mẫu của FMZ. Khái niệm thư viện lớp mẫu có thể được tìm kiếm trong tài liệu API FMZ và chúng tôi sẽ không lặp lại nữa.

Mã thư viện lớp mẫu:

// Global variables
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 the position
        var tradeDirection = type == PD_LONG ? "buy" : "sell"
        // send signals
        msg = symbol + "," + ct + "," + tradeDirection + "," + Math.abs(delta)        
    } else if (delta < 0) {
        // close the position
        var tradeDirection = type == PD_LONG ? "closebuy" : "closesell"
        if (nowAmount <= 0) {
            Log("no positions found")
            return 
        }
        // send signals
        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 the 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 future-following is supported"
    }

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

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

        // monitor positions
        var keyInitRefPosAmount = "refPos-" + exIndex + "-" + symbol + "-" + ct    // refPos-exIndex-symbol-contractType
        var initRefPosAmount = mapInitRefPosAmount[keyInitRefPosAmount]
        if (!initRefPosAmount) {
            // no initialization data, initialize it
            mapInitRefPosAmount[keyInitRefPosAmount] = getPosAmount(_C(ex.GetPosition), ct)
            initRefPosAmount = mapInitRefPosAmount[keyInitRefPosAmount]
        }

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

        // detect changes
        if (!(longPosDelta == 0 && shortPosDelta == 0)) {
            // Perform long positions
            if (longPosDelta != 0) {
                Log(ex.GetName(), ex.GetLabel(), symbol, ct, "Perform long position-following, changes in volume:", longPosDelta)
                follow(nowRefPosAmount, symbol, ct, PD_LONG, longPosDelta)
            }
            // Perform short positions
            if (shortPosDelta != 0) {
                Log(ex.GetName(), ex.GetLabel(), symbol, ct, "Perform short position-following, changes in volume:", shortPosDelta)
                follow(nowRefPosAmount, symbol, ct, PD_SHORT, shortPosDelta)
            }

            // Update after performing the order-following operation
            mapInitRefPosAmount[keyInitRefPosAmount] = nowRefPosAmount
        }

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

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

// Example of the strategy call that references the template class library
function main() {
    // Clear all logs
    LogReset(1)

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

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

    // Timed trading interval
    var tradeInterval = 1000 * 60 * 3        // Trade for every three minutes to observe the order-following signals
    var lastTradeTS = new Date().getTime()
    
    while (true) {
        // Other logic of the strategy...

        // Simulated trading triggers for testing
        var ts = new Date().getTime()
        if (ts - lastTradeTS > tradeInterval) {
            Log("Trade the simulation order-leading strategies, position changes", "#FF0000")
            exchanges[0].SetDirection("buy")
            exchanges[0].Buy(-1, 1)
            lastTradeTS = ts
        }

        // Interface functions that use templates
        $.PosMonitor(0, "ETH_USDT", "swap")    // Multiple monitors can be set up to monitor different exchange objects on the order-following strategy  
        var tbl = $.getTbl()
        
        // Display status bar
        LogStatus(_D(), "\n" + "`" + JSON.stringify(tbl) + "`")
        Sleep(1000)
    }
}

Thiết kế rất đơn giản, thư viện lớp có 2 chức năng. Khi một chiến lược giao dịch lập trình trên nền tảng FMZ tham chiếu thư viện lớp mẫu củaOrder Synchronization Management System Class Library (Single Server). Sau đó chiến lược có thể sử dụng các hàm sau đây.

  • $. Positor Monitor. Mục đích của chức năng này là để theo dõi các 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 thị trường bot thực sự được đặt trong các tham số của mẫu: thư viện lớp Order Synchronization Management System (Single Server).

  • $.getTbl Trở lại dữ liệu đồng bộ được theo dõi.

Ví dụ sử dụng là trongmainchức năng của mẫu thư viện lớp hệ thống quản lý đồng bộ hóa lệnh (Single Server):

// Example of the strategy call that references the template class library
function main() {
    // Clear all logs
    LogReset(1)

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

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

    // Timed trading interval
    var tradeInterval = 1000 * 60 * 3        // Trade for every three minutes to observe the order-following signals
    var lastTradeTS = new Date().getTime()
    
    while (true) {
        // Other logic of the strategy...

        // Simulated trading triggers for testing
        var ts = new Date().getTime()
        if (ts - lastTradeTS > tradeInterval) {
            Log("Trade the simulation order-leading strategies, position changes", "#FF0000")
            exchanges[0].SetDirection("buy")
            exchanges[0].Buy(-1, 1)
            lastTradeTS = ts
        }

        // Interface functions by using templates
        $.PosMonitor(0, "ETH_USDT", "swap")    // Multiple monitors can be set up to monitor different exchange objects on the order-following strategy  
        var tbl = $.getTbl()
        
        // Display status bar
        LogStatus(_D(), "\n" + "`" + JSON.stringify(tbl) + "`")
        Sleep(1000)
    }
}

Một thư viện lớp mẫu cũng có thể tự tạo ra một bot thực sự chiến lược, thường được sử dụng để kiểm tra thư viện lớp mẫu, chẳng hạn như kiểm tra mẫu.mainhàm trong một 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 bằng cách sử dụng OKEX demo để thử nghiệm, bạn cần cấu hình OKEX demo API KEY trên FMZ như một tài khoản tham chiếu (đưa ra lệnh), và nó bắt đầu chuyển sang demo trong chức năng chính. Sau đó đặt cặp giao dịch thành ETH_USDT và đặt hợp đồng để trao đổi. Sau đó nó đi vào vòng lặp while. Trong vòng lặp, một lệnh được đặt mỗi 3 phút để mô phỏng việc 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, tham số đầu tiên của hàm này được truyền đến 0, có nghĩa là để theo dõi các giao dịch đối tượng trao đổi[0], theo dõi cặp giao dịch ETH_USDT, hợp đồng trao đổi.$.getTbl()để có được thông tin biểu đồ, 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, chúng ta có thể thấy rằng chúng ta có thể làm cho chiến lược có khả năng theo dõi vị trí của một loài nhất định, và vị trí thay đổi để gửi tin nhắn bằng cách sử dụng$.PosMonitor(0, "ETH_USDT", "swap")trong một chiến lược tham chiếu đến mẫu.

Trước khi thử nghiệm, chúng tôi sẽ giải thích các thiết kế các tham số chiến lược củaOrder Synchronization Management System Class Library (Single Server). Chúng ta vừa nói về cách sử dụng chức năng giao diện của một mẫu để nâng cấp một chiến lược để có chức năng dẫn đầu lệnh. Câu hỏi về việc gửi ai được cấu hình bởi các tham số củaOrder Synchronization Management System Class Library (Single Server).

img

Chúng ta có thể thấy rằng có 5 tham số, hỗ trợ lên đến 5 đẩy (nó có thể được mở rộng bởi chính mình nếu nó cần tăng), các tham số mặc định là các chuỗi trống, đó là, không được xử lý.

  • nhãn Một nhãn cho một tài khoản đồng bộ, nó được sử dụng để thiết lập một nhãn cho một tài khoản với một tên có thể được thiết lập theo ý muốn.

  • robotId Robot ID, ID củaOrder Synchronous Serverbot thực được tạo bởi chủ sở hữu tài khoản đồng bộ.

  • accessKey Truy cập API mở rộngKey của FMZ

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

Mã tạm thời của Hệ thống quản lý đồng bộ hóa đơn hàng (Synchronous Server):

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

img

Chúng ta có thể thấy rằng bot thực sự của chủ tài khoản đồng bộ hóa đã nhận được tin nhắn:ETH_USDT,swap,buy,1. Sau đó nó sẽ cho phép chúng tôi để làm cho tự động của chúng tôi theo dõi lệnh trong bước tiếp theo dựa trên các cặp giao dịch, mã hợp đồng, hướng giao dịch, và số tiền trong thông tin.

Cho đến nay,Order Synchronization Management System (Synchronous Server)là mã tạm thời, chúng ta sẽ tiếp tục khám phá thiết kế của nó trong số tiếp theo.


Có liên quan

Thêm nữa