Analisis Strategi LeeksReaper ((1)

Penulis:Lydia, Dicipta: 2022-11-04 15:42:26, Dikemas kini: 2023-09-15 21:08:48

img

Analisis Strategi LeeksReaper ((1)

Baru-baru ini, terdapat perbincangan hangat mengenaiprint moneySatu strategi lama telah memasuki mata Quant lagi: LeeksReaper. Prinsip perdagangan robotprint moneyOleh itu, saya membaca semula strategi asal dengan teliti sekali lagi dan melihat versi pemindahan OKCoin pemindahan di FMZ Quant. Strategi leeksreaper yang dipindahkan berdasarkan platform FMZ Quant dianalisis untuk meneroka idea strategi. supaya pengguna platform dapat mempelajari idea strategi ini. Dalam artikel ini, kita akan menganalisis lebih lanjut dari aspek idea strategi dan niat untuk meminimumkan kandungan membosankan yang berkaitan dengan pengaturcaraan.

Kod sumber [Transplanting OKCoin LeeksReaper] strategi:

function LeeksReaper() {
    var self = {}
    self.numTick = 0
    self.lastTradeId = 0
    self.vol = 0
    self.askPrice = 0
    self.bidPrice = 0
    self.orderBook = {Asks:[], Bids:[]}
    self.prices = []
    self.tradeOrderId = 0
    self.p = 0.5
    self.account = null
    self.preCalc = 0
    self.preNet = 0

    self.updateTrades = function() {
        var trades = _C(exchange.GetTrades)
        if (self.prices.length == 0) {
            while (trades.length == 0) {
                trades = trades.concat(_C(exchange.GetTrades))
            }
            for (var i = 0; i < 15; i++) {
                self.prices[i] = trades[trades.length - 1].Price
            }
        }
        self.vol = 0.7 * self.vol + 0.3 * _.reduce(trades, function(mem, trade) {
            // Huobi not support trade.Id
            if ((trade.Id > self.lastTradeId) || (trade.Id == 0 && trade.Time > self.lastTradeId)) {
                self.lastTradeId = Math.max(trade.Id == 0 ? trade.Time : trade.Id, self.lastTradeId)
                mem += trade.Amount
            }
            return mem
        }, 0)

    }
    self.updateOrderBook = function() {
        var orderBook = _C(exchange.GetDepth)
        self.orderBook = orderBook
        if (orderBook.Bids.length < 3 || orderBook.Asks.length < 3) {
            return
        }
        self.bidPrice = orderBook.Bids[0].Price * 0.618 + orderBook.Asks[0].Price * 0.382 + 0.01
        self.askPrice = orderBook.Bids[0].Price * 0.382 + orderBook.Asks[0].Price * 0.618 - 0.01
        self.prices.shift()
        self.prices.push(_N((orderBook.Bids[0].Price + orderBook.Asks[0].Price) * 0.35 +
            (orderBook.Bids[1].Price + orderBook.Asks[1].Price) * 0.1 +
            (orderBook.Bids[2].Price + orderBook.Asks[2].Price) * 0.05))
    }
    self.balanceAccount = function() {
        var account = exchange.GetAccount()
        if (!account) {
            return
        }
        self.account = account
        var now = new Date().getTime()
        if (self.orderBook.Bids.length > 0 && now - self.preCalc > (CalcNetInterval * 1000)) {
            self.preCalc = now
            var net = _N(account.Balance + account.FrozenBalance + self.orderBook.Bids[0].Price * (account.Stocks + account.FrozenStocks))
            if (net != self.preNet) {
                self.preNet = net
                LogProfit(net)
            }
        }
        self.btc = account.Stocks
        self.cny = account.Balance
        self.p = self.btc * self.prices[self.prices.length-1] / (self.btc * self.prices[self.prices.length-1] + self.cny)
        var balanced = false
        
        if (self.p < 0.48) {
            Log("Start balance", self.p)
            self.cny -= 300
            if (self.orderBook.Bids.length >0) {
                exchange.Buy(self.orderBook.Bids[0].Price + 0.00, 0.01)
                exchange.Buy(self.orderBook.Bids[0].Price + 0.01, 0.01)
                exchange.Buy(self.orderBook.Bids[0].Price + 0.02, 0.01)
            }
        } else if (self.p > 0.52) {
            Log("Start balance", self.p)
            self.btc -= 0.03
            if (self.orderBook.Asks.length >0) {
                exchange.Sell(self.orderBook.Asks[0].Price - 0.00, 0.01)
                exchange.Sell(self.orderBook.Asks[0].Price - 0.01, 0.01)
                exchange.Sell(self.orderBook.Asks[0].Price - 0.02, 0.01)
            }
        }
        Sleep(BalanceTimeout)
        var orders = exchange.GetOrders()
        if (orders) {
            for (var i = 0; i < orders.length; i++) {
                if (orders[i].Id != self.tradeOrderId) {
                    exchange.CancelOrder(orders[i].Id)
                }
            }
        }
    }

    self.poll = function() {
        self.numTick++
        self.updateTrades()
        self.updateOrderBook()
        self.balanceAccount()
        
        var burstPrice = self.prices[self.prices.length-1] * BurstThresholdPct
        var bull = false
        var bear = false
        var tradeAmount = 0
        if (self.account) {
            LogStatus(self.account, 'Tick:', self.numTick, ', lastPrice:', self.prices[self.prices.length-1], ', burstPrice: ', burstPrice)
        }
        
        if (self.numTick > 2 && (
            self.prices[self.prices.length-1] - _.max(self.prices.slice(-6, -1)) > burstPrice ||
            self.prices[self.prices.length-1] - _.max(self.prices.slice(-6, -2)) > burstPrice && self.prices[self.prices.length-1] > self.prices[self.prices.length-2]
            )) {
            bull = true
            tradeAmount = self.cny / self.bidPrice * 0.99
        } else if (self.numTick > 2 && (
            self.prices[self.prices.length-1] - _.min(self.prices.slice(-6, -1)) < -burstPrice ||
            self.prices[self.prices.length-1] - _.min(self.prices.slice(-6, -2)) < -burstPrice && self.prices[self.prices.length-1] < self.prices[self.prices.length-2]
            )) {
            bear = true
            tradeAmount = self.btc
        }
        if (self.vol < BurstThresholdVol) {
            tradeAmount *= self.vol / BurstThresholdVol
        }
        
        if (self.numTick < 5) {
            tradeAmount *= 0.8
        }
        
        if (self.numTick < 10) {
            tradeAmount *= 0.8
        }
        
        if ((!bull && !bear) || tradeAmount < MinStock) {
            return
        }
        var tradePrice = bull ? self.bidPrice : self.askPrice
        while (tradeAmount >= MinStock) {
            var orderId = bull ? exchange.Buy(self.bidPrice, tradeAmount) : exchange.Sell(self.askPrice, tradeAmount)
            Sleep(200)
            if (orderId) {
                self.tradeOrderId = orderId
                var order = null
                while (true) {
                    order = exchange.GetOrder(orderId)
                    if (order) {
                        if (order.Status == ORDER_STATE_PENDING) {
                            exchange.CancelOrder(orderId)
                            Sleep(200)
                        } else {
                            break
                        }
                    }
                }
                self.tradeOrderId = 0
                tradeAmount -= order.DealAmount
                tradeAmount *= 0.9
                if (order.Status == ORDER_STATE_CANCELED) {
                    self.updateOrderBook()
                    while (bull && self.bidPrice - tradePrice > 0.1) {
                        tradeAmount *= 0.99
                        tradePrice += 0.1
                    }
                    while (bear && self.askPrice - tradePrice < -0.1) {
                        tradeAmount *= 0.99
                        tradePrice -= 0.1
                    }
                }
            }
        }
        self.numTick = 0
    }
    return self
}

function main() {
    var reaper = LeeksReaper()
    while (true) {
        reaper.poll()
        Sleep(TickInterval)
    }
}

Gambaran keseluruhan strategi

Secara amnya, apabila anda mendapat strategi untuk belajar, anda harus melihat struktur keseluruhan program terlebih dahulu. kod strategi tidak terlalu panjang, dengan kurang daripada 200 baris kod, ia sangat ringkas, dan strategi asal sangat dipulihkan, hampir sama. kod strategi berjalan darimain()keseluruhan kod strategi, kecualimain(), adalah fungsi bernamaLeeksReaper().LeeksReaper()fungsi sangat mudah difahami, ia boleh difahami sebagai pembina modul logik strategi leeksreaper (sebuah objek).LeeksReaper()bertanggungjawab untuk membina logik perdagangan leeksheaper.

Kata kunci:

img

· Barisan pertama strategimainfungsi:var reaper = LeeksReaper(), kod menyatakan pembolehubah tempatanreaperdan kemudian memanggil fungsi LeeksReaper() untuk membina objek logik strategi yang menetapkan nilai kepadareaper.

Langkah strategi seterusnyamainfungsi:

while (true) {
    reaper.poll()
    Sleep(TickInterval)
}

Masukkanwhilegelung tanpa henti dan terus melaksanakan fungsi pemprosesanpoll()daripadareaperobjek,poll()fungsi adalah tepat di mana logik utama strategi perdagangan terletak dan keseluruhan program strategi mula melaksanakan logik perdagangan lagi dan lagi. Mengenai barisanSleep(TickInterval), ia mudah difahami, ia adalah untuk mengawal masa rehat selepas setiap pelaksanaan logik dagangan keseluruhan, dengan tujuan untuk mengawal kekerapan putaran logik dagangan.

MenganalisisLeeksReaper()pembina

Lihat bagaimanaLeeksReaper()fungsi membina objek logik strategi.

PeraturanLeeksReaper()fungsi bermula dengan mengisytiharkan objek kosong,var self = {}, dan semasa pelaksanaanLeeksReaper()fungsi akan secara beransur-ansur menambah beberapa kaedah dan sifat kepada objek kosong ini, akhirnya menyelesaikan pembinaan objek ini dan mengembalikannya (iaitu langkahmain()fungsi di dalamvar reaper = LeeksReaper(), objek yang dikembalikan ditetapkan kepadareaper).

Tambah atribut kepadaselfobjek Seterusnya, saya menambah banyak ciri kepadaself. Saya akan menerangkan setiap atribut seperti berikut, yang boleh memahami tujuan dan niat atribut dan pembolehubah ini dengan cepat, memudahkan pemahaman strategi, dan mengelakkan menjadi keliru apabila melihat kod.

    self.numTick = 0         # It is used to record the number of transactions not triggered when the poll function is called. When the order is triggered and the order logic is executed, self.numTick is reset to 0
    self.lastTradeId = 0     # The transaction record ID of the order that has been transacted in the transaction market. This variable records the current transaction record ID of the market
    self.vol = 0             # Reference to the trading volume of each market inspection after weighted average calculation (market data is obtained once per loop, which can be interpreted as a time of market inspection)
    self.askPrice = 0        # The bill of lading price of the sales order can be understood as the price of the listing order after the strategy is calculated
    self.bidPrice = 0        # Purchase order bill of lading price
    self.orderBook = {Asks:[], Bids:[]}    # Record the currently obtained order book data, that is, depth data (sell one... sell n, buy one... buy n)
    self.prices = []                       # An array that records the prices on the time series after the calculation of the first three weighted averages in the order book, which means that each time the first three weighted averages of the order book are stored, they are placed in an array and used as a reference for subsequent strategy trading signals, so the variable name is prices, in plural form, indicating a set of prices
    self.tradeOrderId = 0    # Record the order ID after the current bill of lading is placed
    self.p = 0.5             # Position proportion: when the value of currency accounts for exactly half of the total asset value, the value is 0.5, that is, the equilibrium state
    self.account = null      # Record the account asset data, which is returned by the GetAccount() function
    self.preCalc = 0         # Record the timestamp of the last time when the revenue was calculated, in milliseconds, to control the frequency of triggering the execution of the revenue calculation code
    self.preNet = 0          # Record current return values

Tambah kaedah kepada objek diri

Selepas menambah sifat-sifat ini kepada diri sendiri, mula menambah kaedah untukselfobjek supaya objek ini boleh melakukan beberapa kerja dan mempunyai beberapa fungsi.

Fungsi pertama menambah:

    self.updateTrades = function() {
        var trades = _C(exchange.GetTrades)  # Call the FMZ encapsulated interface GetTrades to obtain the latest market transaction data
        if (self.prices.length == 0) {       # When self.prices.length == 0, the self.prices array needs to be filled with numeric values, which will be triggered only when the strategy starts running
            while (trades.length == 0) {     # If there is no recent transaction record in the market, the while loop will keep executing until the latest transaction data is available and update the trades variable
                trades = trades.concat(_C(exchange.GetTrades))   # concat is a method of JS array type, which is used to concatenate two arrays, here is to concatenate the "trades" array and the array data returned by "_C(exchange.GetTrades)" into one array
            }
            for (var i = 0; i < 15; i++) {   # Fill in data to self.prices, and fill in 15 pieces of latest transaction prices
                self.prices[i] = trades[trades.length - 1].Price
            }
        }
        self.vol = 0.7 * self.vol + 0.3 * _.reduce(trades, function(mem, trade) {  # _. Reduce function is used for iterative calculation to accumulate the amount of the latest transaction records
            // Huobi not support trade.Id
            if ((trade.Id > self.lastTradeId) || (trade.Id == 0 && trade.Time > self.lastTradeId)) {
                self.lastTradeId = Math.max(trade.Id == 0 ? trade.Time : trade.Id, self.lastTradeId)
                mem += trade.Amount
            }
            return mem
        }, 0)

    }

FungsiupdateTradesadalah untuk mendapatkan data transaksi pasaran terkini dan melakukan beberapa pengiraan berdasarkan data dan merekodkannya untuk digunakan dalam logik strategi berikutnya. Komen baris demi baris yang saya tulis dalam kod di atas secara langsung. Untuk_.reduce, seseorang yang tidak mempunyai pembelajaran pengaturcaraan asas mungkin keliru._.reduceadalah fungsi perpustakaan Underscore.js. Strategi FMZJS menyokong perpustakaan ini, jadi ia sangat mudah untuk pengiraan berulang. Pautan data Underscore.js (https://underscorejs.net/#reduce)

Maknanya juga sangat mudah, contohnya:

function main () {
   var arr = [1, 2, 3, 4]
   var sum = _.reduce(arr, function(ret, ele){
       ret += ele
       
       return ret
   }, 0)

   Log("sum:", sum)    # sum = 10
}

Maksudnya, tambah setiap nombor dalam array[1, 2, 3, 4]Kembali ke strategi kami, kami menambah nilai jumlah dagangan setiap data rekod urus niaga dalamtradesDapatkan jumlah jumlah transaksi terkiniself.vol = 0.7 * self.vol + 0.3 * _.reduce (...), di sini kita menggunakan...untuk menggantikan kod. Ia tidak sukar untuk melihat pengiraanself.voladalah juga purata bertingkat. iaitu, jumlah dagangan yang baru dihasilkan menyumbang 30% daripada jumlah keseluruhan, dan jumlah dagangan terberat terakhir menyumbang 70%. nisbah ini ditetapkan oleh pengarang strategi secara buatan dan ia mungkin berkaitan dengan peraturan pasaran. Mengenai soalan anda, bagaimana jika antara muka untuk mendapatkan data urus niaga terkini kembali kepada data lama duplikat, maka data yang saya mendapat adalah salah, dan ia tidak akan bermakna? jangan risau. masalah ini telah dipertimbangkan dalam reka bentuk strategi, jadi kod ini mempunyai:

if ((trade.Id > self.lastTradeId) || (trade.Id == 0 && trade.Time > self.lastTradeId)) {
    ...
}

penghakiman. Ia boleh dinilai berdasarkan ID transaksi dalam rekod transaksi. Pengumpulan hanya dicetuskan apabila ID lebih besar daripada ID rekod terakhir, atau jika antara muka pertukaran tidak memberikan ID, iaitu,trade.Id == 0, gunakan stempel masa dalam rekod transaksi untuk menilai.self.lastTradeIdmenyimpan stempel masa rekod urus niaga bukannya ID.

Fungsi kedua menambah:

    self.updateOrderBook = function() {
        var orderBook = _C(exchange.GetDepth)
        self.orderBook = orderBook
        if (orderBook.Bids.length < 3 || orderBook.Asks.length < 3) {
            return
        }
        self.bidPrice = orderBook.Bids[0].Price * 0.618 + orderBook.Asks[0].Price * 0.382 + 0.01
        self.askPrice = orderBook.Bids[0].Price * 0.382 + orderBook.Asks[0].Price * 0.618 - 0.01
        self.prices.shift()
        self.prices.push(_N((orderBook.Bids[0].Price + orderBook.Asks[0].Price) * 0.35 +
            (orderBook.Bids[1].Price + orderBook.Asks[1].Price) * 0.1 +
            (orderBook.Bids[2].Price + orderBook.Asks[2].Price) * 0.05))
    }

Seterusnya, mari kita lihat fungsiupdateOrderBook. Dari nama fungsi, kita dapat melihat bahawa ia digunakan untuk mengemas kini buku pesanan. Walau bagaimanapun, ia tidak mengemas kini buku pesanan sahaja. Fungsi mula memanggil fungsi FMZ APIGetDepth()untuk mendapatkan data buku pesanan pasaran semasa (menjual satu... menjual n, membeli satu... membeli n), dan merakam data buku pesanan dalamself.orderBook. Seterusnya, menilai jika pesanan pembelian dan pesanan jualan data buku pesanan kurang daripada 3, jika ya, fungsi tidak sah akan dikembalikan terus.

Selepas itu, dua data dikira:

· Mengira harga bill of lading Harga bill of lading juga dikira dengan menggunakan kaedah purata bertingkat. Apabila mengira pesanan pembelian, berat yang diberikan kepada harga pembelian yang paling dekat dengan harga transaksi adalah 61.8% (0.618) dan berat yang diberikan kepada harga jualan yang paling dekat dengan harga transaksi adalah 38.2% (0.382) Apabila mengira harga bill of lading bill of sale, berat yang sama diberikan kepada harga jualan yang paling dekat dengan harga transaksi.

· Mengemas kini harga purata bertingkat tiga peringkat pertama buku pesanan pada siri masa Untuk tiga peringkat pertama harga pesanan pembelian dan penjualan dalam buku pesanan, purata tertimbang dikira. Berat peringkat pertama adalah 0.7, berat peringkat kedua adalah 0.2, dan berat peringkat ketiga adalah 0.1. Seseorang mungkin berkata, Oh, tidak, terdapat 0.7, 0.2, 0.1 dalam kod. Mari kita meluaskan pengiraan:

(Buy one+Sell one) * 0.35+(Buy two+Sell two) * 0.1+(Buy three+Sell three) * 0.05
->
(Buy one+sell one)/2 * 2 * 0.35+(Buy two+sell two)/2 * 2 * 0.1+(Buy three+sell three)/2 * 2 * 0.05
->
(Buy one+sell one)/2 * 0.7+(Buy two+sell two)/2 * 0.2+(Buy three+sell three)/2 * 0.1
->
Average price of the first level * 0.7+average price of the second level * 0.2+average price of the third level * 0.1

Seperti yang dapat kita lihat di sini, harga yang dihitung akhirnya sebenarnya merupakan tindak balas kepada kedudukan harga pertengahan pembukaan ketiga di pasaran semasa. Kemudian gunakan harga dikira ini untuk mengemas kini arrayself.prices, menendang salah satu data tertua (melaluishift()fungsi) dan mengemas kini salah satu data terbaru ke dalamnya (melaluipush()fungsi, shift dan push fungsi adalah kaedah objek array bahasa JS, anda boleh memeriksa data JS untuk butiran).self.prices, yang merupakan aliran data dengan urutan siri masa.

Jadi mari kita berehat di sini, dan kita akan melihat anda edisi seterusnya ~


Berkaitan

Lebih lanjut