Giao dịch số lượng tiền điện tử cho người mới bắt đầu - đưa bạn đến gần số lượng tiền điện tử (6)

Tác giả:Lydia., Tạo: 2022-08-05 17:13:26, Cập nhật: 2023-09-21 21:02:17

img

Trong bài viết trước đây, chúng tôi đã cùng nhau tạo ra một chiến lược lưới đơn giản. Trong bài viết này, chúng tôi đã nâng cấp và mở rộng chiến lược này thành một chiến lược lưới điểm đa loài, và để cho chiến lược này được thử nghiệm trong thực tế. Mục đích không phải là để tìm ra một Holy Grail, mà là để thảo luận về các vấn đề và giải pháp khác nhau khi thiết kế chiến lược. Bài viết này sẽ giải thích một số kinh nghiệm của tôi trong việc thiết kế chiến lược này. Nội dung của bài viết này hơi phức tạp và nó đòi hỏi một nền tảng nhất định trong lập trình.

Suy nghĩ thiết kế dựa trên nhu cầu chiến lược

Bài viết này, giống như bài trước, vẫn thảo luận về thiết kế dựa trên FMZ Quant (FMZ.COM).

  • Nhiều loài Nói thẳng ra, tôi nghĩ rằng chiến lược lưới điện này không chỉ có thểBTC_USDT, nhưng cũngLTC_USDT/EOS_USDT/DOGE_USDT/ETC_USDT/ETH_USDTDù sao, các cặp giao dịch tại chỗ và các loại muốn chạy đều được giao dịch trên lưới cùng một lúc.

    Hmm~ Thật tốt khi nắm bắt được thị trường biến động của nhiều loài. Yêu cầu nghe có vẻ rất đơn giản, và vấn đề xuất hiện khi thiết kế.

      1. Đầu tiên, các báo giá thị trường của nhiều loại được thu được. Đây là vấn đề đầu tiên phải giải quyết. Sau khi tham khảo tài liệu API của sàn giao dịch, tôi thấy rằng hầu hết các sàn giao dịch cung cấp giao diện thị trường tổng hợp. Được rồi, sử dụng giao diện thị trường tổng hợp để lấy dữ liệu.
      1. Vấn đề thứ hai gặp phải là tài sản tài khoản. Bởi vì đó là một chiến lược đa loài, nó là cần thiết để xem xét quản lý của mỗi tài sản cặp giao dịch riêng biệt. Và chúng ta cần phải có được dữ liệu cho tất cả các tài sản cùng một lúc, và ghi lại chúng. Tại sao chúng ta cần phải có được dữ liệu tài sản tài khoản? Tại sao chúng ta cần phải tách các hồ sơ của mỗi cặp? Bởi vì bạn cần đánh giá các tài sản có sẵn khi đặt hàng. Và bạn cần để tính toán lợi nhuận, nó cũng là cần thiết để ghi lại một tài sản tài khoản ban đầu dữ liệu đầu tiên, sau đó có được tài sản tài sản tài khoản vãng lai và so sánh nó với ban đầu một để tính toán lợi nhuận và tổn thất? May mắn thay, giao diện tài khoản tài sản của sàn giao dịch thường trả về tất cả dữ liệu tài sản tiền tệ, chúng ta chỉ cần lấy nó một lần, và sau đó xử lý dữ liệu.
      1. Thiết kế tham số chiến lược. Thiết kế tham số của nhiều loài khá khác với thiết kế tham số của một loại, mặc dù logic giao dịch của mỗi loại đa loại giống nhau, nhưng có thể các tham số trong quá trình giao dịch khác nhau. Ví dụ, trong chiến lược lưới, bạn có thể muốn giao dịch 0.01 BTC mỗi lần khi giao dịch cặp BTC_USDT, nhưng rõ ràng là không phù hợp khi sử dụng tham số này (giao dịch 0.01 coin) khi giao dịch cặp DOGE_USDT. Tất nhiên, bạn cũng có thể xử lý số tiền USDT. Nhưng vẫn sẽ có vấn đề. Nếu bạn muốn giao dịch 1000U cho BTC_USDT và 10U cho DOGE_USDT thì sao? Nhu cầu không bao giờ có thể được thỏa mãn. Có thể có ai đó sẽ suy nghĩ về vấn đề này và sau đó hỏi: Tôi có thể thiết lập một số bộ tham số để kiểm soát các tham số của các cặp giao dịch khác nhau được thực hiện riêng biệt. Điều này vẫn không đủ linh hoạt để đáp ứng nhu cầu, bao nhiêu bộ tham số là tốt để thiết lập? ba bộ tham số được thiết lập, nếu tôi muốn làm cho 4 giống? tôi phải sửa đổi chiến lược và tăng các tham số?.. Do đó, khi thiết kế các tham số của chiến lược đa loài, cần phải xem xét đầy đủ nhu cầu của các tham số khác biệt như vậy. Ví dụ:
      ETHUSDT:100:0.002|LTCUSDT:20:0.1
      

      Trong số đó, gaman chia dữ liệu của mỗi loài, có nghĩa làETHUSDT:100:0.002kiểm soát cặp giao dịch ETH_USDT, vàLTCUSDT:20:0.1điều khiển cặp giao dịch LTC_USDT.ETHUSDT:100:0.002, trong đó ETHUSDT chỉ ra cặp giao dịch bạn muốn làm, 100 là khoảng cách lưới, 0,002 là số tiền ETH được giao dịch trong mỗi lưới, và : là để chia dữ liệu này (tất nhiên, các quy tắc tham số này được thiết kế bởi nhà thiết kế chiến lược, bạn có thể thiết kế bất cứ điều gì theo nhu cầu của mình). Các chuỗi này chứa thông tin tham số của mỗi loài bạn muốn làm. Phân tích các chuỗi này trong chiến lược, và gán giá trị cho các biến của chiến lược để kiểm soát logic giao dịch của mỗi loài. Làm thế nào để phân tích nó?

      function main() {
          var net = []  // The recorded grid parameters, use the data when running to the grid trading logic
          var params = "ETHUSDT:100:0.002|LTCUSDT:20:0.1"
          var arrPair = params.split("|")
          _.each(arrPair, function(pair) {
              var arr = pair.split(":")
              var symbol = arr[0]              // Trading pair name
              var diff = parseFloat(arr[1])    // Grid spacing
              var amount = parseFloat(arr[2])  // Grid order volume
              net.push({symbol : symbol, diff : diff, amount : amount})
          })
          Log("Grid parameter data:", net)
      }
      

      img

      Tất nhiên, bạn cũng có thể sử dụng các chuỗi JSON trực tiếp, đơn giản hơn.

      function main() {        
          var params = '[{"symbol":"ETHUSDT","diff":100,"amount":0.002},{"symbol":"LTCUSDT","diff":20,"amount":0.1}]'
          var net = JSON.parse(params)  // The recorded grid parameters, use the data when running to the grid trading logic        
          _.each(net, function(pair) {
              Log("Trading pairs:", pair.symbol, pair)
          })
      }
      

      img

      1. Độ bền dữ liệu Có một sự khác biệt lớn giữa các chiến lược có thể áp dụng trong thực tế và các chiến lược hướng dẫn. Các chiến lược hướng dẫn trong bài viết trước đây chỉ là một thử nghiệm sơ bộ về logic và thiết kế chiến lược, và có nhiều vấn đề cần xem xét khi nói đến thế giới thực. Trong bot thực, có thể bắt đầu và dừng giao dịch thực. Tại thời điểm này, tất cả dữ liệu trong quá trình hoạt động bot thực sẽ bị mất. Vậy làm thế nào để bot thực khởi động lại để tiếp tục chạy trong trạng thái trước sau khi ngừng? Ở đây, cần phải lưu dữ liệu chính liên tục khi bot thực đang chạy, để dữ liệu có thể được đọc và tiếp tục chạy khi nó được khởi động lại. Bạn có thể sử dụng_G()chức năng trên nền tảng giao dịch định lượng FMZ, hoặc sử dụng chức năng hoạt động cơ sở dữ liệuDBExec(), và bạn có thể kiểm tra tài liệu API FMZ để biết chi tiết.

      Ví dụ, chúng tôi thiết kế một chức năng quét đuôi và sử dụng_G()chức năng để lưu dữ liệu lưới.

      var net = null 
      function main() {  // Strategy main functions
          // Read the stored net first
          net = _G("net")
          
          // ...
      }
      
      function onExit() {
          _G("net", net)
          Log("Perform tail-sweeping processing and save data", "#FF0000")
      }
      
      function onexit() {    // The exit sweep function defined by the platform system, triggered the execution when the real bot is clicked to stop
          onExit()
      }
      
      function onerror() {   // The abnormal exit function defined by the platform system, triggered the execution when the program is abnormal
          onExit()
      }
      
      1. Các giới hạn như độ chính xác số lượng đơn đặt hàng, độ chính xác giá đơn đặt hàng, số lượng đơn đặt hàng tối thiểu và số lượng đơn đặt hàng tối thiểu, v.v.

      Hệ thống backtesting không áp đặt những hạn chế nghiêm ngặt như vậy đối với số tiền đặt hàng và độ chính xác của lệnh, nhưng mỗi sàn giao dịch có thể có các tiêu chuẩn nghiêm ngặt về giá cả và số tiền đặt hàng khi đặt lệnh trong bot thực, và những hạn chế này không giống nhau trong các sàn giao dịch khác nhau. Do đó, có những người mới bắt đầu thử nghiệm trong hệ thống backtesting mà không có vấn đề. Một khi bot thực được khởi động, có nhiều vấn đề khác nhau khi giao dịch được kích hoạt, và sau đó nội dung của thông báo lỗi không được đọc, và nhiều hiện tượng điên rồ xuất hiện.

      Đối với các trường hợp đa loài, yêu cầu này phức tạp hơn. Đối với một chiến lược một loài, bạn có thể thiết kế một tham số để chỉ định thông tin như độ chính xác, nhưng khi thiết kế một chiến lược đa loài, rõ ràng là viết thông tin này vào các tham số sẽ làm cho các tham số rất phồng phồng.

      Tại thời điểm này, bạn cần kiểm tra tài liệu API của sàn giao dịch để xem liệu có thông tin giao diện liên quan đến các cặp giao dịch trong tài liệu sàn giao dịch hay không. Nếu có, bạn có thể thiết kế giao diện truy cập tự động trong chiến lược để có được thông tin như độ chính xác, và cấu hình nó vào thông tin cặp giao dịch liên quan đến giao dịch (tóm lại, độ chính xác hoặc một cái gì đó được lấy từ sàn giao dịch tự động, và sau đó điều chỉnh theo các biến liên quan đến các thông số chiến lược).

      1. Điều chỉnh cho các sàn giao dịch khác nhau Tại sao lại đặt câu hỏi này vào cuối? Bởi vì các giải pháp cho những vấn đề mà chúng tôi đã nói ở trên sẽ mang lại vấn đề cuối cùng, bởi vì chiến lược của chúng tôi có kế hoạch sử dụng giao diện thị trường tổng hợp, truy cập vào độ chính xác cặp giao dịch sàn giao dịch và các dữ liệu thích nghi khác, truy cập vào thông tin tài khoản để đối phó với mỗi cặp giao dịch riêng biệt, các giải pháp này có thể khác nhau rất nhiều từ sàn giao dịch này sang sàn giao dịch khác. Có sự khác biệt trong các cuộc gọi và cơ chế giao diện. Đối với các sàn giao dịch giao ngay, sự khác biệt nhỏ hơn nếu chiến lược lưới được mở rộng sang phiên bản tương lai. Sự khác biệt trong cơ chế của các sàn giao dịch khác nhau thậm chí còn lớn hơn. Một giải pháp là thiết kế thư viện lớp mẫu FMZ. Viết thiết kế trong thư viện lớp để thực hiện những khác biệt này. Giảm sự kết nối giữa chính chiến lược và sàn giao dịch. Nhược điểm của điều này là bạn cần phải viết một thư viện lớp mẫu, và thực hiện nó cụ thể cho mỗi sự khác biệt trao đổi trong mẫu này.

Thiết kế thư viện lớp mẫu

Dựa trên phân tích trên, thư viện lớp mẫu được thiết kế để giảm sự kết nối giữa chiến lược và cơ chế trao đổi và giao diện.

Chúng ta có thể thiết kế thư viện lớp mẫu này như thế này (một phần của mã bị bỏ qua):

function createBaseEx(e, funcConfigure) {
    var self = {}
    self.e = e 
    
    self.funcConfigure = funcConfigure
    self.name = e.GetName()
    self.type = self.name.includes("Futures_") ? "Futures" : "Spot"
    self.label = e.GetLabel()
    
    // Interfaces to be implemented
    self.interfaceGetTickers = null   // Create a function to asynchronously obtain a thread of aggregated market data
    self.interfaceGetAcc = null       // Create a function that asynchronously obtains account data thread
    self.interfaceGetPos = null       // Get a position
    self.interfaceTrade = null        // Create concurrent orders
    self.waitTickers = null           // Waiting for concurrent market data 
    self.waitAcc = null               // Waiting for account concurrent data
    self.waitTrade = null             // Waiting for order concurrent data
    self.calcAmount = null            // Calculate the order volume based on data such as trading pair accuracy
    self.init = null                  // Initialization work, obtaining data such as accuracy
    
    // Execute the configuration function to configure the object
    funcConfigure(self)

    // Check whether the interfaces agreed by configList are implemented
    _.each(configList, function(funcName) {
        if (!self[funcName]) {
            throw "interface" + funcName + "unimplemented"
        }
    })
    
    return self
}

$.createBaseEx = createBaseEx
$.getConfigureFunc = function(exName) {
    dicRegister = {
        "Futures_OKCoin" : funcConfigure_Futures_OKCoin,    // Implementation of OK futures
        "Huobi" : funcConfigure_Huobi,
        "Futures_Binance" : funcConfigure_Futures_Binance,
        "Binance" : funcConfigure_Binance,
        "WexApp" : funcConfigure_WexApp,                    // Implementation of wexApp
    }
    return dicRegister
}

Trong mẫu, nó được viết cho các sàn giao dịch cụ thể, lấy ví dụ về robot mô phỏng FMZ của WexApp:

function funcConfigure_WexApp(self) {
    var formatSymbol = function(originalSymbol) {
        // BTC_USDT
        var arr = originalSymbol.split("_")
        var baseCurrency = arr[0]
        var quoteCurrency = arr[1]
        return [originalSymbol, baseCurrency, quoteCurrency]
    }

    self.interfaceGetTickers = function interfaceGetTickers() {
        self.routineGetTicker = HttpQuery_Go("https://api.wex.app/api/v1/public/tickers")
    }

    self.waitTickers = function waitTickers() {
        var ret = []
        var arr = JSON.parse(self.routineGetTicker.wait()).data
        _.each(arr, function(ele) {
            ret.push({
                bid1: parseFloat(ele.buy), 
                bid1Vol: parseFloat(-1),
                ask1: parseFloat(ele.sell), 
                ask1Vol: parseFloat(-1),
                symbol: formatSymbol(ele.market)[0],
                type: "Spot", 
                originalSymbol: ele.market
            })
        })
        return ret 
    }

    self.interfaceGetAcc = function interfaceGetAcc(symbol, updateTS) {
        if (self.updateAccsTS != updateTS) {
            self.routineGetAcc = self.e.Go("GetAccount")
        }
    }

    self.waitAcc = function waitAcc(symbol, updateTS) {
        var arr = formatSymbol(symbol)
        var ret = null 
        if (self.updateAccsTS != updateTS) {
            ret = self.routineGetAcc.wait().Info
            self.bufferGetAccRet = ret 
        } else {
            ret = self.bufferGetAccRet
        }
        if (!ret) {
            return null 
        }        
        var acc = {symbol: symbol, Stocks: 0, FrozenStocks: 0, Balance: 0, FrozenBalance: 0, originalInfo: ret}
        _.each(ret.exchange, function(ele) {
            if (ele.currency == arr[1]) {
                // baseCurrency
                acc.Stocks = parseFloat(ele.free)
                acc.FrozenStocks = parseFloat(ele.frozen)
            } else if (ele.currency == arr[2]) {
                // quoteCurrency
                acc.Balance = parseFloat(ele.free)
                acc.FrozenBalance = parseFloat(ele.frozen)
            }
        })
        return acc
    }

    self.interfaceGetPos = function interfaceGetPos(symbol, price, initSpAcc, nowSpAcc) {
        var symbolInfo = self.getSymbolInfo(symbol)
        var sumInitStocks = initSpAcc.Stocks + initSpAcc.FrozenStocks
        var sumNowStocks = nowSpAcc.Stocks + nowSpAcc.FrozenStocks
        var diffStocks = _N(sumNowStocks - sumInitStocks, symbolInfo.amountPrecision)
        if (Math.abs(diffStocks) < symbolInfo.min / price) {
            return []
        }
        return [{symbol: symbol, amount: diffStocks, price: null, originalInfo: {}}]
    }

    self.interfaceTrade = function interfaceTrade(symbol, type, price, amount) {
        var tradeType = ""
        if (type == self.OPEN_LONG || type == self.COVER_SHORT) {
            tradeType = "bid"
        } else {
            tradeType = "ask"
        }
        var params = {
            "market": symbol,
            "side": tradeType,
            "amount": String(amount),
            "price" : String(-1),
            "type" : "market"
        }
        self.routineTrade = self.e.Go("IO", "api", "POST", "/api/v1/private/order", self.encodeParams(params))
    }

    self.waitTrade = function waitTrade() {
        return self.routineTrade.wait()
    }

    self.calcAmount = function calcAmount(symbol, type, price, amount) {
        // Obtain trading pair information
        var symbolInfo = self.getSymbolInfo(symbol)
        if (!symbol) {
            throw symbol + ", the trading pair information cannot be checked"
        }
        var tradeAmount = null 
        var equalAmount = null  // Number of coins recorded
        if (type == self.OPEN_LONG || type == self.COVER_SHORT) {
            tradeAmount = _N(amount * price, parseFloat(symbolInfo.pricePrecision))
            // Check the minimum trading volume
            if (tradeAmount < symbolInfo.min) {
                Log(self.name, " tradeAmount:", tradeAmount, "less than", symbolInfo.min)
                return false 
            }
            equalAmount = tradeAmount / price
        } else {
            tradeAmount = _N(amount, parseFloat(symbolInfo.amountPrecision))
            // Check the minimum trading volume
            if (tradeAmount < symbolInfo.min / price) {
                Log(self.name, " tradeAmount:", tradeAmount, "less than", symbolInfo.min / price)
                return false 
            }
            equalAmount = tradeAmount
        }
        return [tradeAmount, equalAmount]
    }

    self.init = function init() {   // Functions that deal with conditions such as accuracy automatically
        var ret = JSON.parse(HttpQuery("https://api.wex.app/api/v1/public/markets"))
        _.each(ret.data, function(symbolInfo) {
            self.symbolsInfo.push({
                symbol: symbolInfo.pair,
                amountPrecision: parseFloat(symbolInfo.basePrecision),
                pricePrecision: parseFloat(symbolInfo.quotePrecision),
                multiplier: 1,
                min: parseFloat(symbolInfo.minQty),
                originalInfo: symbolInfo
            })
        })        
    }
}

Sau đó sử dụng mẫu này trong một chiến lược là đơn giản:

function main() {
    var fuExName = exchange.GetName()
    var fuConfigureFunc = $.getConfigureFunc()[fuExName]
    var ex = $.createBaseEx(exchange, fuConfigureFunc)

    var arrTestSymbol = ["LTC_USDT", "ETH_USDT", "EOS_USDT"]
    var ts = new Date().getTime()
    
    // Test to get tickers
    ex.goGetTickers()
    var tickers = ex.getTickers()
    Log("tickers:", tickers)
    
    // Test to obtain account information
    ex.goGetAcc(symbol, ts)
    
    _.each(arrTestSymbol, function(symbol) {        
        _.each(tickers, function(ticker) {
            if (symbol == ticker.originalSymbol) {
                // print ticker data
                Log(symbol, ticker)
            }
        })

        // print asset data
        var acc = ex.getAcc(symbol, ts)
        Log("acc:", acc.symbol, acc)
    })
}

Chiến lược robot thực sự

Nó rất đơn giản để thiết kế và viết một chiến lược dựa trên mẫu trên. Toàn bộ chiến lược là khoảng 300+ dòng và thực hiện một chiến lược lưới đa loài tiền kỹ thuật số.

img

img

Nó đang mất tiền.T_T, mã nguồn sẽ không được phát hành trong thời gian này.

Dưới đây là một vài mã đăng ký, nếu bạn quan tâm, bạn có thể sử dụng wexApp để thử:

Buy address: https://www.fmz.com/m/s/284507
Registration code: 
adc7a2e0a2cfde542e3ace405d216731
f5db29d05f57266165ce92dc18fd0a30
1735dca92794943ddaf277828ee04c27
0281ea107935015491cda2b372a0997d
1d0d8ef1ea0ea1415eeee40404ed09cc

Một chút hơn 200 U, khi tôi mới bắt đầu chạy, tôi gặp một thị trường một bên lớn, nhưng tôi phục hồi từ từ. Sự ổn định không tệ, nó đã không thay đổi kể từ ngày 27 tháng 5, và mạng lưới tương lai không dám thử tạm thời.


Có liên quan

Thêm nữa