Pemula, periksa Membawa Anda ke Cryptocurrency Quantitative Trading (6)

Penulis:Ninabadass, Dibuat: 2022-04-21 18:13:03, Diperbarui: 2022-04-22 12:00:05

Pemula, periksa Membawa Anda ke Cryptocurrency Quantitative Trading (6)

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

Memikirkan Desain Berdasarkan Keperluan Strategi

Dalam artikel ini, sama seperti yang sebelumnya, kita membahas tentang desain berdasarkan FMZ Quant Trading Platform (FMZ.COM).

  • Simbol ganda Sejujurnya, saya ingin strategi grid tidak hanyaBTC_USDT, tapi jugaLTC_USDT/EOS_USDT/DOGE_USDT/ETC_USDT/ETH_USDTBagaimanapun, untuk pasangan perdagangan spot, mengoperasikan perdagangan grid dari semua simbol yang ingin Anda perdagangan pada saat yang sama.

    Ya, rasanya enak untuk menangkap penawaran pasar bergetar dari beberapa simbol.
    Meskipun persyaratan ini terdengar sederhana, menjadi sulit ketika Anda mulai merancang.

      1. Pertama, mendapatkan kutipan pasar dari beberapa simbol. Ini adalah masalah pertama yang harus diselesaikan. Setelah membaca dokumentasi API platform, saya menemukan bahwa platform biasanya menyediakan antarmuka agregat. Oke, kita menggunakan antarmuka data pasar agregat untuk mendapatkan data.
      1. Masalah kedua adalah aset akun. karena kita ingin melakukan strategi multi-simbol, kita harus mempertimbangkan untuk mengelola aset masing-masing pasangan perdagangan secara terpisah, dan mendapatkan semua data aset dan mencatatnya sekali. mengapa kita harus mendapatkan data aset akun? dan mengapa juga mencatat setiap pasangan perdagangan secara terpisah?

    Karena Anda perlu menilai aset yang tersedia saat menempatkan pesanan, bukankah perlu untuk mendapatkan data sebelum penilaian? Selain itu, pengembalian harus dihitung. Haruskah kita mencatat data aset akuntansi awal terlebih dahulu? dan kemudian mendapatkan data aset dari rekening arus dan menghitung laba rugi dengan membandingkan dengan yang awal? Untungnya, antarmuka akun aset dari platform biasanya mengembalikan semua data aset mata uang, jadi kita hanya perlu mendapatkannya sekali, dan kemudian memproses data.

      1. Desain parameter strategi. Desain parameter strategi multi-simbol sangat berbeda dari desain parameter satu simbol tunggal, karena bahkan logika perdagangan setiap simbol dalam strategi multi-simbol sama, mungkin saja parameter setiap simbol selama perdagangan berbeda. Misalnya, dalam strategi grid, Anda mungkin ingin memperdagangkan 0,01 BTC setiap kali melakukan pasangan perdagangan BTC_USDT, tetapi jelas tidak pantas untuk menggunakan parameter ini (perdagangan 0,01 mata uang) saat melakukan DOGE_USDT. Tentu saja, Anda juga dapat memproses dengan jumlah USDT. Tapi masih akan ada masalah. Bagaimana jika Anda ingin memperdagangkan 1000U oleh BTC_USDT dan 10U oleh DOGE_USDT? Permintaan tidak pernah dapat terpenuhi. Mungkin, beberapa siswa mungkin berpikir tentang pertanyaan, dan mengusulkan bahwa, Saya dapat mengatur lebih banyak kelompok parameter, dan secara terpisah mengontrol parameter dari pasangan perdagangan yang berbeda untuk dioperasikan. Yang masih tidak dapat secara fleksibel memenuhi kebutuhan, untuk berapa banyak kelompok parameter harus ditetapkan? kita menetapkan 3 kelompok; bagaimana jika kita ingin mengoperasikan 4 simbol? apakah kita perlu memodifikasi strategi dan meningkatkan parameter? Oleh karena itu, berpikir sepenuhnya tentang kebutuhan untuk diferensiasi ketika merancang parameter strategi multi-simbol.
        Misalnya:
      ETHUSDT:100:0.002|LTCUSDT:20:0.1
      

      digunakan untuk membagi data dari setiap simbol, menunjukkan bahwaETHUSDT:100:0.002mengontrol pasangan perdagangan ETH_USDT, danLTCUSDT:20:0.1Mengontrol pasangan perdagangan LTC_USDT. goba di tengah memainkan peran segmentasi. Di dalamETHUSDT:100:0.002, ETHUSDT mewakili pasangan perdagangan yang ingin Anda operasikan; 100 adalah jarak grid; 0.002 adalah jumlah ETH yang diperdagangkan dari setiap grid; : digunakan untuk membagi data yang disebutkan di atas (pasti, aturan parameter dibuat oleh perancang strategi; Anda dapat merancang apa pun yang Anda inginkan berdasarkan kebutuhan Anda).
      String ini sudah berisi informasi parameter dari setiap simbol yang perlu Anda operasikan. Anda dapat menganalisis string, dan menetapkan nilai untuk variabel dalam strategi, untuk mengontrol logika perdagangan dari setiap simbol. Bagaimana untuk menganalisis? Mari kita gunakan contoh yang disebutkan di atas.

      function main() {
          var net = []  // the recorded grid parameters; when specifically running the grid trading logic, use the data from here 
          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 amount 
              net.push({symbol : symbol, diff : diff, amount : amount})
          })
          Log("Grid parameter data:", net)
      }
      

      img

      Lihat, kita telah menganalisis parameter. tentu saja, Anda dapat langsung menggunakan string JSON, yang lebih mudah.

      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; when specifically running the grid trading logic, use the data from here         
          _.each(net, function(pair) {
              Log("Trading pair:", pair.symbol, pair)
          })
      }
      

      img

      1. Persistensi Data Ada perbedaan besar antara strategi praktis dan strategi pengajaran. Strategi pengajaran dalam artikel sebelumnya hanya untuk tes awal logika dan desain strategi. Ada lebih banyak masalah yang perlu dikhawatirkan ketika benar-benar menjalankan strategi dalam bot. Saat menjalankan bot, bot dapat dimulai dan dihentikan. Pada saat itu, semua data selama menjalankan bot akan hilang. Jadi, bagaimana melanjutkan status sebelumnya saat memulai kembali bot, setelah bot dihentikan? Di sini, perlu untuk terus-menerus menyimpan data kunci ketika bot sedang berjalan, sehingga data dapat dibaca dan bot terus berjalan ketika bot di-restart. Anda bisa menggunakan_G()Fungsi pada FMZ Quant, atau gunakan fungsi operasiDBExec()dalam database, dan Anda dapat menanyakan dokumentasi FMZ API untuk rincian.

      Sebagai contoh, kita ingin merancang fungsi pembersihan dengan menggunakan fungsi_G(), untuk menyimpan data grid.

      var net = null 
      function main() {  // strategy main function 
          // first read the stored net 
          net = _G("net")
          
          // ...
      }
      
      function onExit() {
          _G("net", net)
          Log("Execute the clean-up processing, and save the data", "#FF0000")
      }
      
      function onexit() {    // the onexit function defined by the platform system, which will be triggered when clicking the bot to stop 
          onExit()
      }
      
      function onerror() {   // the onerror function defined by the platform system, which will be triggered when the program exception occurs 
          onExit()
      }
      
      1. Batas presisi jumlah pesanan, presisi harga pesanan, volume pesanan minimum, jumlah pesanan minimum, dll.

      Sistem backtest tidak memiliki batasan yang ketat pada volume order dan presisi order; tetapi dalam bot, setiap platform memiliki standar yang ketat untuk harga order dan volume order, dan pasangan perdagangan yang berbeda memiliki batas yang berbeda. Oleh karena itu, pemula sering menguji OKEX dalam sistem backtest. Setelah strategi dijalankan pada bot, ada berbagai masalah ketika perdagangan dipicu, dan kemudian isi pesan kesalahan tidak dibaca, dan berbagai fenomena gila muncul.

      Untuk kasus multi-simbol, persyaratan lebih rumit. Untuk strategi simbol tunggal, Anda dapat merancang parameter untuk menentukan informasi seperti presisi. Namun, ketika Anda merancang strategi multi-simbol, jelas bahwa menulis informasi ke dalam parameter akan membuat parameter sangat membosankan.

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

      1. Adaptasi Platform yang Berbeda Mengapa masalah tersebut disebutkan di akhir? Pengolahan semua masalah yang telah kita sebutkan di atas akan mengarah ke masalah terakhir. Karena strategi kami berencana untuk menggunakan antarmuka pasar agregat, solusi seperti mengakses presisi pasangan perdagangan platform dan penyesuaian data lainnya, serta mengakses informasi akun untuk memproses setiap pasangan perdagangan secara terpisah, dll., Akan membawa perbedaan besar karena platform yang berbeda. Ada perbedaan dalam panggilan antarmuka dan perbedaan dalam mekanisme. Untuk platform spot, perbedaannya relatif kecil jika strategi grid diperluas ke versi berjangka. Perbedaan dalam mekanisme setiap platform bahkan lebih besar. Salah satu solusi adalah merancang perpustakaan template FMZ; menulis desain implementasi diferensiasi di perpustakaan untuk mengurangi kopling antara strategi itu sendiri dan platform. Kerugian dari itu adalah bahwa Anda perlu menulis perpustakaan template, dan dalam template ini, secara khusus menerapkan diferensiasi berdasarkan setiap platform.

Desain Perpustakaan Templat

Berdasarkan analisis di atas, kami merancang perpustakaan template untuk mengurangi kopling antara strategi, mekanisme platform dan antarmuka.
Kita dapat merancang perpustakaan template seperti ini (bagian dari 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()
    
    // the interfaces that need to be implemented 
    self.interfaceGetTickers = null   // create a function that asynchronously obtains the aggregated market quote threads
    self.interfaceGetAcc = null       // create a function that asynchronously obtains the account data threads 
    self.interfaceGetPos = null       // obtain positions 
    self.interfaceTrade = null        // create concurrent orders 
    self.waitTickers = null           // wait for the concurrent market quote data  
    self.waitAcc = null               // wait for the account concurrent data 
    self.waitTrade = null             // wait for order concurrent data
    self.calcAmount = null            // calculate the order amount according to the trading pair precision and other data 
    self.init = null                  // initialization; obtain the precision and other data 
    
    // execute the configuration function, to configure objects 
    funcConfigure(self)

    // detect whether all the interfaces arranged by configList can be implemented 
    _.each(configList, function(funcName) {
        if (!self[funcName]) {
            throw "interface" + funcName + "not implemented"
        }
    })
    
    return self
}

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

Dalam templat, terapkan penulisan kode yang ditujukan untuk bentuk permainan tertentu; ambil FMZ 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 the trading pair information 
        var symbolInfo = self.getSymbolInfo(symbol)
        if (!symbol) {
            throw symbol + ",trading pair information not found"
        }
        var tradeAmount = null 
        var equalAmount = null  // record the symbol amount  
        if (type == self.OPEN_LONG || type == self.COVER_SHORT) {
            tradeAmount = _N(amount * price, parseFloat(symbolInfo.pricePrecision))
            // detect the minimum trading amount 
            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))
            // detect the minimum trading amount 
            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() {   // the function that automatically processes conditions like precision, etc.  
        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
            })
        })        
    }
}

Ini akan sangat mudah untuk menggunakan template dalam strategi:

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 obtain the market quotes 
    ex.goGetTickers()
    var tickers = ex.getTickers()
    Log("tickers:", tickers)
    
    // test to obtain the account information 
    ex.goGetAcc(symbol, ts)
    
    _.each(arrTestSymbol, function(symbol) {        
        _.each(tickers, function(ticker) {
            if (symbol == ticker.originalSymbol) {
                // print the market quote data 
                Log(symbol, ticker)
            }
        })

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

Bot Strategi

Ini sangat sederhana untuk merancang dan menulis strategi berdasarkan template di atas. Seluruh strategi memiliki sekitar lebih dari 300 baris kode. Itu menerapkan strategi grid multi-simbol spot cryptocurrency.

img

img

Saat ini, ia memiliki kerugianT_T, jadi kode sumber tidak akan diberikan.

Ada beberapa kode pendaftaran; jika Anda tertarik, Anda dapat mencobanya di wexApp:

Purchase Address: https://www.fmz.com/m/s/284507
Registration Code:
adc7a2e0a2cfde542e3ace405d216731
f5db29d05f57266165ce92dc18fd0a30
1735dca92794943ddaf277828ee04c27
0281ea107935015491cda2b372a0997d
1d0d8ef1ea0ea1415eeee40404ed09cc

Ada hanya lebih dari 200 USD, dan ketika bot baru saja dimulai, ia menemukan pasar satu sisi yang besar. Stabilitas strategi ini baik, dan saya tidak memodifikasinya sejak 27 Mei.


Lebih banyak