Sumber daya yang dimuat... Pemuatan...

Cryptocurrency Quantitative Trading for Beginners - Membawa Anda Lebih Dekat dengan Cryptocurrency Quantitative (6)

Penulis:FMZ~Lydia, Dibuat: 2022-08-05 17:13:26, Diperbarui: 2023-09-21 21:02:17

img

Dalam artikel sebelumnya, kami membuat strategi grid sederhana bersama-sama. Dalam artikel ini, kami memperbarui dan memperluas strategi ini menjadi strategi grid spot multi spesies, dan biarkan strategi ini diuji dalam praktek. Tujuannya bukan untuk menemukan holy grail, tetapi untuk membahas berbagai masalah dan solusi saat merancang strategi. Artikel ini akan menjelaskan beberapa pengalaman saya dalam merancang strategi ini. Isi artikel ini sedikit rumit dan membutuhkan dasar tertentu dalam pemrograman.

Pemikiran desain berdasarkan kebutuhan strategis

Artikel ini, seperti yang sebelumnya, masih membahas desain berdasarkan FMZ Quant (FMZ.COM).

  • Berbagai spesies Untuk mengatakannya secara langsung, saya pikir strategi grid ini tidak hanya dapatBTC_USDT, tapi jugaLTC_USDT/EOS_USDT/DOGE_USDT/ETC_USDT/ETH_USDTBagaimanapun, pasangan perdagangan spot dan varietas yang ingin berjalan semua diperdagangkan di grid pada saat yang sama.

    Hmm~ Rasanya enak untuk menangkap pasar volatile dari beberapa spesies. Persyaratan ini terdengar sangat sederhana, dan masalahnya datang saat merancang.

      1. Pertama, kutipan pasar dari berbagai varietas diperoleh. Ini adalah masalah pertama yang harus diselesaikan. Setelah berkonsultasi dengan dokumentasi API bursa, saya menemukan bahwa sebagian besar bursa menyediakan antarmuka pasar agregat. OK, gunakan antarmuka pasar agregat untuk mendapatkan data.
      1. Masalah kedua yang dihadapi adalah aset akun. Karena ini adalah strategi multi spesies, perlu untuk mempertimbangkan manajemen setiap aset pasangan perdagangan secara terpisah. Dan kita perlu mendapatkan data untuk semua aset sekaligus, dan merekam mereka. Mengapa kita perlu mendapatkan data aset akun? Mengapa kita perlu memisahkan catatan dari setiap pasangan? Karena Anda perlu menilai aset yang tersedia ketika menempatkan pesanan. Dan Anda perlu untuk menghitung profit, apakah juga perlu untuk mencatat data aset akun awal pertama, kemudian mendapatkan data aset rekening berjalan dan membandingkannya dengan yang awal untuk menghitung laba rugi? Untungnya, antarmuka akun aset dari bursa biasanya mengembalikan semua data aset mata uang, kita hanya perlu mendapatkannya sekali, dan kemudian memproses data.
      1. Desain parameter strategi. Desain parameter multi-spesies sangat berbeda dari desain parameter single-variety, meskipun logika perdagangan setiap varietas multi-variety sama, mungkin parameter selama perdagangan berbeda. Misalnya, dalam strategi grid, Anda mungkin ingin memperdagangkan 0,01 BTC setiap kali melakukan pasangan perdagangan BTC_USDT, tetapi jelas tidak tepat untuk menggunakan parameter ini (perdagangan 0,01 koin) saat melakukan DOGE_USDT. Tentu saja, Anda juga dapat berurusan dengan jumlah USDT. Tapi masih akan ada masalah. Bagaimana jika Anda ingin memperdagangkan 1000U untuk BTC_USDT dan 10U untuk DOGE_USDT? Permintaan tidak pernah dapat terpenuhi. Mungkin ada seseorang yang akan berpikir tentang masalah ini dan kemudian bertanya: Saya dapat mengatur beberapa set parameter untuk mengontrol parameter dari pasangan perdagangan yang berbeda untuk dilakukan secara terpisah. Ini masih tidak cukup fleksibel untuk memenuhi kebutuhan, berapa banyak set parameter yang baik untuk ditetapkan? Tiga set parameter ditetapkan, bagaimana jika saya ingin membuat 4 varietas? Apakah saya harus memodifikasi strategi dan meningkatkan parameter?.. Oleh karena itu, ketika merancang parameter strategi multi spesies, perlu untuk sepenuhnya mempertimbangkan kebutuhan parameter yang berbeda tersebut. Misalnya:
      ETHUSDT:100:0.002|LTCUSDT:20:0.1
      

      Di antara mereka, gaman membagi data dari setiap spesies, yang berarti bahwaETHUSDT:100:0.002mengontrol pasangan perdagangan ETH_USDT, danLTCUSDT:20:0.1Mengontrol pasangan perdagangan LTC_USDT.ETHUSDT:100:0.002, di mana ETHUSDT menunjukkan pasangan perdagangan apa yang ingin Anda lakukan, 100 adalah jarak kisi, 0,002 adalah jumlah koin ETH yang diperdagangkan di setiap kisi, dan : adalah untuk membagi data ini (tentu saja, aturan parameter ini dibuat oleh desainer strategi, Anda dapat merancang apa pun sesuai dengan kebutuhan Anda). String ini berisi informasi parameter dari setiap spesies yang ingin Anda lakukan. Parse string ini dalam strategi, dan atribut nilai untuk variabel strategi untuk mengontrol logika perdagangan dari setiap spesies. Bagaimana untuk menganalisisnya? Masih menggunakan contoh di atas.

      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

      Dengan melihat ini, parameter dianalisis. tentu saja, Anda juga dapat menggunakan string JSON secara langsung, yang lebih sederhana.

      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. Daya tahan data Ada juga perbedaan besar antara strategi yang dapat diterapkan dalam praktek dan strategi tutorial. Strategi tutorial dalam artikel sebelumnya hanya tes awal logika dan desain strategi, dan ada lebih banyak masalah yang perlu dipertimbangkan ketika datang ke dunia nyata. Dalam bot nyata, dimungkinkan untuk memulai dan menghentikan perdagangan nyata. Pada saat ini, semua data selama operasi bot nyata akan hilang. Jadi bagaimana membuat bot nyata dihidupkan kembali untuk terus berjalan dalam status sebelumnya setelah dihentikan? Di sini, perlu untuk menyimpan data kunci terus-menerus ketika bot yang sebenarnya sedang berjalan, sehingga data dapat dibaca dan terus berjalan ketika di-restart. Anda bisa menggunakan_G()Fungsi di Platform Perdagangan Kuantitatif FMZ, atau menggunakan fungsi operasi basis dataDBExec(), dan Anda dapat memeriksa dokumentasi FMZ API untuk rincian.

      Sebagai contoh, kita merancang fungsi sapu ekor dan menggunakan_G()fungsi untuk menyimpan data grid.

      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. Batas seperti presisi jumlah pesanan, presisi harga pesanan, jumlah pesanan minimum, dan jumlah pesanan minimum, dll.

      Sistem backtesting tidak memberlakukan pembatasan yang ketat pada jumlah pesanan dan akurasi pesanan, tetapi setiap bursa dapat memiliki standar yang ketat untuk harga dan jumlah pesanan saat menempatkan pesanan di bot nyata, dan pembatasan ini tidak sama di bursa yang berbeda. Oleh karena itu, ada pemula yang menguji dalam sistem backtesting tanpa masalah. Setelah bot nyata diluncurkan, ada berbagai masalah ketika perdagangan dipicu, dan kemudian isi pesan kesalahan tidak dibaca, dan berbagai fenomena gila muncul.

      Untuk kasus multi-spesies, persyaratan ini lebih rumit. Untuk strategi satu spesies, Anda dapat merancang parameter untuk menentukan informasi seperti akurasi, tetapi ketika merancang strategi multi-spesies, jelas bahwa menulis informasi ini ke dalam parameter akan membuat parameter sangat kembung.

      Pada saat ini, Anda perlu memeriksa dokumentasi API dari bursa untuk melihat apakah ada informasi antarmuka yang terkait dengan pasangan perdagangan dalam dokumentasi bursa. Jika ada, Anda dapat merancang antarmuka akses otomatis dalam strategi untuk mendapatkan informasi seperti akurasi, dan mengkonfigurasinya ke dalam informasi pasangan perdagangan yang terlibat dalam perdagangan (singkatnya, akurasi atau sesuatu yang diperoleh dari bursa secara otomatis, dan kemudian disesuaikan dengan variabel yang terkait dengan parameter strategi).

      1. Adaptasi untuk pertukaran yang berbeda Kenapa kau menaruh pertanyaan ini di akhir? Karena solusi untuk masalah-masalah yang kita bicarakan di atas akan membawa masalah terakhir, karena strategi kami berencana untuk menggunakan antarmuka pasar agregat, akses ke akurasi pasangan perdagangan pertukaran dan data adaptif lainnya, akses ke informasi akun untuk menangani setiap pasangan perdagangan secara terpisah, solusi ini dapat sangat bervariasi dari pertukaran ke pertukaran. Ada perbedaan dalam panggilan antarmuka dan mekanisme. Untuk bursa spot, perbedaannya lebih kecil jika strategi grid diperluas ke versi futures. Perbedaan dalam mekanisme berbagai bursa bahkan lebih besar. Salah satu solusi adalah merancang perpustakaan kelas templat FMZ. Tuliskan desain di perpustakaan kelas untuk menerapkan perbedaan ini. Kurangi kopling antara strategi itu sendiri dan bursa. Kelemahan dari ini adalah bahwa Anda perlu menulis perpustakaan kelas template, dan menerapkannya secara khusus untuk setiap perbedaan pertukaran dalam template ini.

Merancang perpustakaan kelas template

Berdasarkan analisis di atas, perpustakaan kelas templat dirancang untuk mengurangi kopling antara strategi dan mekanisme pertukaran dan antarmuka.

Kita bisa mendesain perpustakaan kelas templat ini seperti ini (sebagian kode dihilangkan):

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
}

Dalam template, ditulis untuk pertukaran tertentu, ambil FMZs simulasi bot WexApp sebagai contoh:

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

Kemudian menggunakan template ini dalam strategi adalah sederhana:

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

Strategi bot nyata

Sangat sederhana untuk merancang dan menulis strategi berdasarkan template di atas. Seluruh strategi sekitar 300+ baris dan menerapkan strategi grid multi-spesies spot mata uang digital.

img

img

Ini kehilangan uang saat iniT_T, kode sumbernya tidak akan dirilis untuk saat ini.

Berikut adalah beberapa kode pendaftaran, jika Anda tertarik, Anda dapat menggunakan wexApp untuk mencobanya:

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

Hanya sekitar 200 U, ketika saya baru saja mulai berlari, saya menemukan pasar satu sisi yang besar, tetapi saya pulih perlahan. Stabilitasnya tidak buruk, tidak ada perubahan sejak 27 Mei, dan grid berjangka tidak berani mencoba sementara.


Berkaitan

Lebih banyak