Perdagangan Kuantitatif Cryptocurrency untuk pemula - Membawa anda lebih dekat dengan Kuantitatif Cryptocurrency (6)

Penulis:Lydia, Dicipta: 2022-08-05 17:13:26, Dikemas kini: 2023-09-21 21:02:17

img

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 spesies, dan biarkan strategi ini diuji dalam amalan. Tujuannya bukan untuk mencari holy grail, tetapi untuk membincangkan pelbagai masalah dan penyelesaian ketika merancang strategi. Artikel ini akan menerangkan beberapa pengalaman saya dalam merancang strategi ini. Kandungan artikel ini agak rumit dan memerlukan asas tertentu dalam pengaturcaraan.

Pemikiran reka bentuk berdasarkan keperluan strategik

Artikel ini, seperti yang sebelumnya, masih membincangkan reka bentuk berdasarkan FMZ Quant (FMZ.COM).

  • Pelbagai spesies Untuk mengatakannya secara terang-terangan, saya fikir strategi grid ini tidak hanya bolehBTC_USDT, tetapi jugaLTC_USDT/EOS_USDT/DOGE_USDT/ETC_USDT/ETH_USDTBagaimanapun, pasangan dagangan spot dan varieti yang ingin berjalan semua didagangkan di grid pada masa yang sama.

    Hmm~ Rasanya baik untuk menangkap pasaran pelbagai spesies. Keperluan itu terdengar sangat mudah, dan masalah datang semasa mereka merancang.

      1. Pertama, sebut harga pasaran pelbagai jenis diperoleh. Ini adalah masalah pertama yang perlu diselesaikan. Selepas melihat dokumentasi API bursa, saya mendapati bahawa kebanyakan bursa menyediakan antara muka pasaran agregat. OK, gunakan antara muka pasaran agregat untuk mendapatkan data.
      1. Masalah kedua yang dihadapi adalah aset akaun. Kerana ia adalah strategi pelbagai spesies, adalah perlu untuk mempertimbangkan pengurusan setiap aset pasangan dagangan secara berasingan. Dan kita perlu mendapatkan data untuk semua aset sekaligus, dan merekodkannya. Mengapa kita perlu mendapatkan data aset akaun? Mengapa kita perlu memisahkan rekod setiap pasangan? Kerana anda perlu menilai aset yang ada semasa membuat pesanan. Dan anda perlu mengira keuntungan, adakah ia juga perlu untuk merakam data aset akaun awal terlebih dahulu, kemudian mendapatkan data aset akaun semasa dan membandingkannya dengan yang awal untuk mengira keuntungan dan kerugian? Nasib baik, antara muka akaun aset bursa biasanya mengembalikan semua data aset mata wang, kita hanya perlu mendapatkannya sekali, dan kemudian memproses data.
      1. Reka bentuk parameter strategi. Reka bentuk parameter pelbagai spesies agak berbeza dari reka bentuk parameter pelbagai tunggal, walaupun logik perdagangan setiap pelbagai pelbagai adalah sama, adalah mungkin bahawa parameter semasa perdagangan adalah berbeza. Sebagai contoh, dalam strategi grid, anda mungkin ingin berdagang 0.01 BTC setiap kali melakukan pasangan perdagangan BTC_USDT, tetapi jelas tidak sesuai untuk menggunakan parameter ini (berdagang 0.01 syiling) ketika melakukan DOGE_USDT. Sudah tentu, anda juga boleh berurusan dengan jumlah USDT. Tetapi masih akan ada masalah. Bagaimana jika anda ingin berdagang 1000U untuk BTC_USDT dan 10U untuk DOGE_USDT? Permintaan tidak pernah dapat dipenuhi. Mungkin ada seseorang yang akan berfikir tentang masalah ini dan kemudian bertanya: Saya boleh menetapkan beberapa set parameter untuk mengawal parameter pasangan dagangan yang berbeza untuk dilakukan secara berasingan. Ini masih tidak cukup fleksibel untuk memenuhi keperluan, berapa set parameter yang baik untuk ditetapkan? Tiga set parameter ditetapkan, bagaimana jika saya mahu membuat 4 jenis? Adakah saya perlu mengubah suai strategi dan meningkatkan parameter?.. Oleh itu, ketika merancang parameter strategi pelbagai spesies, adalah perlu untuk mempertimbangkan sepenuhnya keperluan parameter yang berbeza. Contohnya:
      ETHUSDT:100:0.002|LTCUSDT:20:0.1
      

      Di antara mereka, gaman membahagikan data bagi setiap spesies, yang bermaksud bahawaETHUSDT:100:0.002mengawal pasangan dagangan ETH_USDT, danLTCUSDT:20:0.1Mengendalikan pasangan dagangan LTC_USDT.ETHUSDT:100:0.002, di mana ETHUSDT menunjukkan apa pasangan dagangan yang anda ingin lakukan, 100 adalah jarak grid, 0.002 adalah bilangan duit syiling ETH yang didagangkan dalam setiap grid, dan : adalah untuk membahagikan data ini (tentu saja, peraturan parameter ini dibuat oleh pereka strategi, anda boleh merancang apa sahaja mengikut keperluan anda). String ini mengandungi maklumat parameter setiap spesies yang ingin anda lakukan. Parse string ini dalam strategi, dan menetapkan nilai kepada pembolehubah strategi untuk mengawal logik perdagangan setiap spesies. Bagaimana untuk parse? 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

      Memandangkan ini, parameter dianalisis. sudah tentu, anda juga boleh menggunakan rentetan JSON secara langsung, 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, use the data when running to the grid trading logic        
          _.each(net, function(pair) {
              Log("Trading pairs:", pair.symbol, pair)
          })
      }
      

      img

      1. Ketahanan data Terdapat juga perbezaan besar antara strategi yang boleh digunakan dalam amalan dan strategi tutorial. Strategi tutorial dalam artikel sebelumnya hanyalah ujian awal logika dan reka bentuk strategi, dan terdapat lebih banyak isu yang perlu dipertimbangkan apabila datang ke dunia nyata. Dalam bot sebenar, adalah mungkin untuk memulakan dan menghentikan perdagangan sebenar. Pada masa ini, semua data semasa operasi bot sebenar akan hilang. Jadi bagaimana untuk membuat bot sebenar dimulakan semula untuk terus berjalan dalam status sebelumnya selepas ia berhenti? Di sini, adalah perlu untuk menyimpan data utama berterusan apabila bot sebenar sedang berjalan, supaya data boleh dibaca dan terus berjalan apabila ia dimulakan semula. Anda boleh gunakan_G()fungsi di Platform Dagangan Kuantitatif FMZ, atau menggunakan fungsi operasi pangkalan dataDBExec(), dan anda boleh memeriksa dokumentasi FMZ API untuk butiran.

      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. Had seperti ketepatan kuantiti pesanan, ketepatan harga pesanan, kuantiti pesanan minimum, dan jumlah pesanan minimum, dll.

      Sistem backtesting tidak mengenakan sekatan yang ketat terhadap jumlah pesanan dan ketepatan pesanan, tetapi setiap bursa boleh mempunyai piawaian yang ketat untuk harga dan jumlah pesanan apabila meletakkan pesanan di bot sebenar, dan sekatan ini tidak sama di bursa yang berbeza. Oleh itu, ada pemula yang menguji dalam sistem backtesting tanpa masalah. Setelah bot sebenar dilancarkan, terdapat pelbagai masalah apabila perdagangan dicetuskan, dan kemudian kandungan mesej ralat tidak dibaca, dan pelbagai fenomena gila muncul.

      Untuk kes pelbagai spesies, keperluan ini lebih rumit. Untuk strategi spesies tunggal, anda boleh merancang parameter untuk menentukan maklumat seperti ketepatan, tetapi ketika merancang strategi pelbagai spesies, adalah jelas bahawa menulis maklumat ini ke dalam parameter akan membuat parameter sangat kembung.

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

      1. Penyesuaian untuk pertukaran yang berbeza Kenapa letak soalan ini di akhir? Kerana penyelesaian kepada masalah-masalah yang kita bincangkan di atas akan membawa masalah terakhir, kerana strategi kami merancang untuk menggunakan antara muka pasaran agregat, akses kepada ketepatan pasangan dagangan bursa dan data lain adaptif, akses kepada maklumat akaun untuk menangani setiap pasangan dagangan secara berasingan, penyelesaian ini boleh berbeza-beza dari pertukaran ke pertukaran. Terdapat perbezaan dalam panggilan antara muka dan mekanisme. Untuk pertukaran spot, perbezaannya lebih kecil jika strategi grid dilanjutkan ke versi niaga hadapan. Perbezaan dalam mekanisme pelbagai pertukaran lebih besar lagi. Salah satu penyelesaian adalah untuk merancang perpustakaan kelas templat FMZ. Tulis reka bentuk dalam perpustakaan kelas untuk melaksanakan perbezaan ini. Kurangkan penyambungan antara strategi itu sendiri dan pertukaran. Kelemahan ini adalah bahawa anda perlu menulis perpustakaan kelas templat, dan melaksanakannya secara khusus untuk setiap perbezaan pertukaran dalam templat ini.

Merancang perpustakaan kelas templat

Berdasarkan analisis di atas, perpustakaan kelas templat direka untuk mengurangkan hubungan antara strategi dan mekanisme pertukaran dan antara muka.

Kita boleh merancang perpustakaan kelas templat ini 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()
    
    // 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 templat, ia 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 templat ini dalam strategi adalah mudah:

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 sebenar

Ia adalah sangat mudah untuk merancang dan menulis strategi berdasarkan templat di atas. keseluruhan strategi adalah kira-kira 300+ baris dan melaksanakan mata wang digital spot strategi grid pelbagai spesies.

img

img

Ia kehilangan wang sekarangT_T, kod sumber tidak akan dikeluarkan buat masa ini.

Berikut adalah beberapa kod pendaftaran, jika anda berminat, anda boleh menggunakan wexApp untuk mencuba:

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

Hanya lebih daripada 200 U, ketika saya mula berlari, saya menemui pasaran satu sisi yang besar, tetapi saya pulih perlahan. Stabiliti tidak buruk, ia tidak diubahsuai sejak 27 Mei, dan grid niaga hadapan tidak berani untuk mencuba sementara.


Berkaitan

Lebih lanjut