Pemula, periksa Membawa anda ke Perdagangan Kuantitatif Cryptocurrency (6)

Penulis:Ninabadass, Dicipta: 2022-04-21 18:13:03, Dikemas kini: 2022-04-22 12:00:05

Pemula, periksa Membawa anda ke Perdagangan Kuantitatif Cryptocurrency (6)

Dalam artikel yang lalu, kami membuat strategi grid yang mudah bersama-sama. Dalam artikel ini, kami menaik taraf dan memperluaskan strategi ini menjadi strategi grid spot pelbagai simbol, dan biarkan strategi ini diuji dalam amalan. Tujuannya bukan untuk mencari holy grail, tetapi untuk membincangkan pelbagai masalah dan penyelesaian dalam proses merancang strategi. Artikel ini akan menerangkan beberapa pengalaman saya dalam merancang strategi. Kandungan artikel ini agak rumit dan memerlukan asas tertentu dalam pengaturcaraan.

Berfikir Reka Bentuk Berdasarkan Keperluan Strategi

Dalam artikel ini, sama seperti yang sebelumnya, kita membincangkan mengenai reka bentuk berdasarkan FMZ Quant Trading Platform (FMZ.COM).

  • Simbol Berbilang Untuk jujur, saya mahu strategi grid tidak hanya untukBTC_USDT, tetapi jugaLTC_USDT/EOS_USDT/DOGE_USDT/ETC_USDT/ETH_USDT. Bagaimanapun, untuk pasangan dagangan spot, beroperasi grid perdagangan semua simbol yang anda ingin berdagang pada masa yang sama.

    Ya, ia berasa baik untuk menangkap petikan pasaran bergetar daripada pelbagai simbol.
    Walaupun keperluan itu terdengar mudah, ia menjadi sukar apabila anda mula merancang.

      1. Pertama, mendapatkan sebut harga pasaran pelbagai simbol. Ia adalah masalah pertama yang perlu diselesaikan. Selepas membaca dokumentasi API platform, saya mendapati bahawa platform biasanya menyediakan antara muka agregat. Oke, kita menggunakan antara muka data pasaran agregat untuk mendapatkan data.
      1. Masalah kedua adalah aset akaun. kerana kita ingin melaksanakan strategi multi-simbol, kita harus mempertimbangkan untuk menguruskan aset setiap pasangan dagangan secara berasingan, dan mendapatkan semua data aset dan merekodkannya sekali. mengapa kita harus mendapatkan data aset akaun? dan mengapa juga merekodkan setiap pasangan dagangan secara berasingan?

    Kerana anda perlu menilai aset yang tersedia apabila meletakkan pesanan, tidak perlu mendapatkan data sebelum penilaian? Di samping itu, pulangan perlu dikira. Adakah kita mencatat data aset akaun awal terlebih dahulu? dan kemudian mendapatkan data aset akaun semasa dan mengira keuntungan dan kerugian dengan membandingkan dengan yang awal? Nasib baik, antara muka akaun aset platform biasanya mengembalikan semua data aset mata wang, jadi kita hanya perlu mendapatkannya sekali, dan kemudian memproses data.

      1. Reka bentuk parameter strategi. Reka bentuk parameter strategi multi-simbol agak berbeza dengan reka bentuk parameter satu simbol tunggal, kerana walaupun logik perdagangan setiap simbol dalam strategi multi-simbol adalah sama, adalah mungkin bahawa parameter setiap simbol semasa perdagangan berbeza. Sebagai contoh, dalam strategi grid, anda mungkin ingin berdagang 0.01 BTC setiap kali ketika melakukan pasangan perdagangan BTC_USDT, tetapi jelas tidak sesuai untuk menggunakan parameter ini (berdagang 0.01 mata wang) ketika melakukan DOGE_USDT. Sudah tentu, anda juga boleh memproses dengan jumlah USDT. Tetapi masih akan ada masalah. Bagaimana jika anda ingin berdagang 1000U oleh BTC_USDT dan 10U oleh DOGE_USDT? Permintaan tidak pernah dapat dipenuhi. Mungkin, sesetengah pelajar mungkin berfikir tentang soalan itu, dan mencadangkan bahawa, Saya boleh menetapkan lebih banyak kumpulan parameter, dan secara berasingan mengawal parameter pasangan perdagangan yang berbeza untuk dioperasikan. Yang masih tidak boleh fleksibel memenuhi keperluan, untuk berapa banyak kumpulan parameter yang perlu ditetapkan? kita menetapkan 3 kumpulan; bagaimana jika kita mahu mengendalikan 4 simbol? adakah kita perlu mengubah suai strategi dan meningkatkan parameter? Oleh itu, berfikir sepenuhnya tentang keperluan untuk pembezaan apabila mereka merancang parameter strategi pelbagai simbol.
        Contohnya:
      ETHUSDT:100:0.002|LTCUSDT:20:0.1
      

      digunakan untuk membahagikan data setiap simbol, menunjukkan bahawaETHUSDT:100:0.002mengawal pasangan dagangan ETH_USDT, danLTCUSDT:20:0.1mengawal pasangan dagangan LTC_USDT. goba di tengah memainkan peranan segmen. DalamETHUSDT:100:0.002, ETHUSDT mewakili pasangan dagangan yang anda ingin beroperasi; 100 adalah jarak grid; 0.002 adalah jumlah ETH yang diperdagangkan dari setiap grid; : digunakan untuk membahagikan data yang disebutkan di atas (pasti, peraturan parameter dibuat oleh pereka strategi; anda boleh merancang apa sahaja yang anda mahukan berdasarkan keperluan anda).
      Senar ini sudah mengandungi maklumat parameter setiap simbol yang anda perlukan untuk beroperasi. Anda boleh menganalisis senar, dan menetapkan nilai kepada pembolehubah dalam strategi, untuk mengawal logik perdagangan 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, anda boleh terus menggunakan rentetan 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. Keberlanjutan Data Terdapat perbezaan besar antara strategi praktikal dan strategi pengajaran. Strategi pengajaran dalam artikel sebelumnya hanya untuk ujian awal logika dan reka bentuk strategi. Terdapat lebih banyak masalah yang perlu dibimbangkan apabila benar-benar menjalankan strategi dalam bot. Semasa menjalankan bot, bot boleh dimulakan dan dihentikan. Pada masa itu, semua data semasa menjalankan bot akan hilang. Jadi, bagaimana untuk meneruskan status sebelumnya ketika memulakan semula bot, selepas bot dihentikan? Di sini, adalah perlu untuk terus menyimpan data utama apabila bot sedang berjalan, supaya data dapat dibaca dan bot terus berjalan apabila bot dimulakan semula. Anda boleh menggunakan_G()fungsi pada FMZ Quant, atau menggunakan fungsi operasiDBExec()dalam pangkalan data, dan anda boleh menanyakan dokumentasi FMZ API untuk butiran.

      Sebagai contoh, kita mahu 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. Had pada ketepatan jumlah pesanan, ketepatan harga pesanan, jumlah pesanan minimum, jumlah pesanan minimum, dll.

      Sistem backtest tidak mempunyai had yang ketat pada jumlah pesanan dan ketepatan pesanan; tetapi dalam bot, setiap platform mempunyai piawaian yang ketat untuk harga pesanan dan jumlah pesanan, dan pasangan dagangan yang berbeza mempunyai had yang berbeza. Oleh itu, pemula sering menguji OKEX dalam sistem backtest. Setelah strategi dijalankan pada bot, terdapat pelbagai masalah apabila perdagangan dicetuskan, dan kemudian kandungan mesej ralat tidak dibaca, dan pelbagai fenomena gila muncul.

      Untuk kes pelbagai simbol, keperluan lebih rumit. Untuk strategi simbol tunggal, anda boleh merancang parameter untuk menentukan maklumat seperti ketepatan. Walau bagaimanapun, apabila anda merancang strategi pelbagai simbol, adalah jelas bahawa menulis maklumat ke dalam parameter akan membuat parameter sangat membosankan.

      Pada masa ini, anda perlu menyemak dokumentasi API platform untuk melihat sama ada terdapat antara muka untuk maklumat berkaitan pasangan dagangan dalam dokumentasi. Jika terdapat antara muka ini, anda boleh merancang antara muka akses automatik dalam strategi untuk mendapatkan maklumat seperti ketepatan, dan mengkonfigurasinya ke dalam maklumat pasangan dagangan dalam perdagangan (singkatnya, ketepatan diperoleh secara automatik dari platform, dan kemudian disesuaikan dengan pembolehubah yang berkaitan dengan parameter strategi).

      1. Penyesuaian Platform Berbeza Kenapa masalah itu disebut pada akhir? Pengolahan semua masalah yang telah kita sebutkan di atas akan membawa kepada masalah terakhir. Oleh kerana strategi kami merancang untuk menggunakan antara muka pasaran agregat, penyelesaian seperti mengakses ketepatan pasangan dagangan platform dan penyesuaian data lain, serta mengakses maklumat akaun untuk memproses setiap pasangan dagangan secara berasingan, dll, akan membawa perbezaan besar kerana platform yang berbeza. Terdapat perbezaan dalam panggilan antara muka dan perbezaan dalam mekanisme. Untuk platform spot, perbezaannya agak kecil jika strategi grid dilanjutkan ke versi niaga hadapan. Perbezaan dalam mekanisme setiap platform lebih besar lagi. Satu penyelesaian adalah untuk merancang perpustakaan templat FMZ; menulis reka bentuk pelaksanaan pembezaan dalam perpustakaan untuk mengurangkan penyambungan antara strategi itu sendiri dan platform. Kelemahannya ialah anda perlu menulis perpustakaan templat, dan dalam templat ini, secara khusus melaksanakan pembezaan berdasarkan setiap platform.

Merancang Perpustakaan Templat

Berdasarkan analisis di atas, kami merancang perpustakaan templat untuk mengurangkan penyambungan antara strategi, mekanisme platform dan antara muka.
Kita boleh merancang perpustakaan templat seperti ini (sebahagian kod 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, melaksanakan penulisan kod yang bertujuan untuk bentuk permainan tertentu; ambil bot simulasi FMZ 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
            })
        })        
    }
}

Ia akan menjadi sangat mudah untuk menggunakan templat 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

Ia sangat mudah untuk merancang dan menulis strategi berdasarkan templat di atas. Seluruh strategi mempunyai kira-kira lebih daripada 300 baris kod. Yang melaksanakan strategi grid multi-simbol cryptocurrency spot.

img

img

Sekarang, ia mempunyai kerugian.T_T, jadi kod sumber tidak akan disediakan.

Terdapat beberapa kod pendaftaran; jika anda berminat, anda boleh mencuba mereka di wexApp:

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

Terdapat hanya lebih daripada 200 USD, dan apabila bot baru bermula, ia datang di atas pasaran satu sisi yang hebat. Ia memerlukan masa untuk menampung kerugian. Keuntungan terbesar strategi grid spot adalah: rasa selamat untuk tidur! Stabiliti strategi itu baik, dan saya tidak mengubahnya sejak 27 Mei.


Lebih lanjut