Analisis Strategi LeeksReaper (1)

Penulis:Lydia, Dibuat: 2022-11-04 15:42:26, Diperbarui: 2023-09-15 21:08:48

img

Analisis Strategi LeeksReaper (1)

Baru-baru ini, ada perdebatan hangat tentangprint moneySebuah strategi yang sangat lama telah masuk ke mata Quants lagi: LeeksReaper. Prinsip perdagangan robotprint moneyJadi, saya membaca kembali strategi asli dengan hati-hati lagi dan melihat versi transplantasi OKCoin transplantasi leeksreaper di FMZ Quant. Strategi pemetik bawang putih yang ditransplantasikan berdasarkan platform FMZ Quant dianalisis untuk mengeksplorasi gagasan strategi. Dalam artikel ini, kita akan menganalisis lebih lanjut dari aspek ide strategi dan niat untuk meminimalkan konten membosankan yang terkait dengan pemrograman.

Kode sumber dari [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)
    }
}

Tinjauan strategi

Secara umum, ketika Anda mendapatkan strategi untuk dipelajari, Anda harus melihat struktur program secara keseluruhan terlebih dahulu. kode strategi tidak terlalu panjang, dengan kurang dari 200 baris kode, sangat ringkas, dan strategi asli sangat dipulihkan, hampir sama. kode strategi berjalan darimain()seluruh kode strategi, kecualimain(), adalah fungsi bernamaLeeksReaper().LeeksReaper()fungsi sangat mudah dipahami, dapat dipahami sebagai konstruktor dari leeksreaper modul logika strategi (objek).LeeksReaper()bertanggung jawab untuk membangun logika perdagangan leeksheaper.

Kata kunci:

img

· Jalur pertama dari strategimainFungsi:var reaper = LeeksReaper(), kode menyatakan variabel lokalreaperdan kemudian memanggil fungsi LeeksReaper() untuk membangun objek logika strategi yang menetapkan nilai untukreaper.

Langkah berikutnya dari strategimainFungsi:

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

Masukkan awhileloop tanpa akhir dan terus menjalankan fungsi pemrosesanpoll()darireaperobjek, yangpoll()fungsi adalah persis di mana logika utama strategi perdagangan terletak dan seluruh program strategi mulai melaksanakan logika perdagangan lagi dan lagi. Adapun garisSleep(TickInterval), mudah dimengerti, adalah untuk mengontrol waktu jeda setelah setiap pelaksanaan keseluruhan logika perdagangan, dengan tujuan untuk mengontrol frekuensi rotasi logika perdagangan.

MenganalisisLeeksReaper()konstruktor

Lihatlah bagaimanaLeeksReaper()fungsi membangun objek logika strategi.

PeraturanLeeksReaper()fungsi dimulai dengan mendeklarasikan objek kosong,var self = {}, dan selama pelaksanaanLeeksReaper()fungsi akan secara bertahap menambahkan beberapa metode dan atribut untuk objek kosong ini, akhirnya menyelesaikan konstruksi objek ini dan mengembalikannya (yaitu, langkahmain()fungsi di dalamvar reaper = LeeksReaper(), objek yang dikembalikan ditugaskan untukreaper).

Tambahkan atribut keselfobjek Selanjutnya, saya menambahkan banyak atribut untukself. Saya akan menjelaskan setiap atribut sebagai berikut, yang dapat memahami tujuan dan niat dari atribut dan variabel ini dengan cepat, memfasilitasi pemahaman strategi, dan menghindari bingung ketika melihat kode.

    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

Tambahkan metode ke objek diri

Setelah menambahkan atribut ini untuk diri sendiri, mulai menambahkan metode untukselfobjek sehingga objek ini dapat melakukan beberapa pekerjaan dan memiliki beberapa fungsi.

Fungsi pertama menambahkan:

    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 pasar terbaru dan melakukan beberapa perhitungan berdasarkan data dan merekamnya untuk digunakan dalam logika berikutnya dari strategi. Komentar baris demi baris yang saya tulis dalam kode di atas secara langsung. Untuk_.reduce, seseorang yang tidak memiliki pembelajaran dasar pemrograman mungkin bingung._.reduceadalah fungsi dari pustaka Underscore.js. Strategi FMZJS mendukung pustaka ini, sehingga sangat nyaman untuk perhitungan iteratif.https://underscorejs.net/#reduce)

Artinya juga sangat sederhana, untuk contoh:

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
}

Artinya, tambahkan setiap angka dalam array[1, 2, 3, 4]Kembali ke strategi kami, kami menambahkan nilai volume perdagangan dari setiap data catatan transaksi ditradesDapatkan total volume transaksi terbaruself.vol = 0.7 * self.vol + 0.3 * _.reduce (...), di sini kita menggunakan...Tidak sulit untuk melihat perhitungan dariself.voljuga merupakan rata-rata tertimbang. yaitu volume perdagangan yang baru dihasilkan menyumbang 30% dari total, dan volume perdagangan tertimbang terakhir menyumbang 70%. rasio ini ditetapkan oleh penulis strategi secara artifisial dan mungkin terkait dengan aturan pasar. Adapun pertanyaan Anda, bagaimana jika antarmuka untuk mendapatkan data transaksi terbaru kembali ke duplikat data lama, maka data yang saya dapatkan adalah salah, dan tidak akan berarti? Jangan khawatir. masalah ini dipertimbangkan dalam desain strategi, jadi kode memiliki:

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

penghakiman. dapat dinilai berdasarkan ID transaksi dalam catatan transaksi. akumulasi dipicu hanya ketika ID lebih besar dari ID catatan terakhir, atau jika antarmuka pertukaran tidak memberikan ID, yaitu,trade.Id == 0, gunakan timestamp dalam catatan transaksi untuk menilai.self.lastTradeIdmenyimpan timestamp dari catatan transaksi bukan ID.

Fungsi kedua menambahkan:

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

Selanjutnya, mari kita lihat fungsiupdateOrderBook. Dari nama fungsi, kita dapat melihat bahwa fungsi ini digunakan untuk memperbarui buku pesanan. Namun, tidak hanya memperbarui buku pesanan. Fungsi ini mulai memanggil fungsi FMZ APIGetDepth()untuk memperoleh data buku pesanan pasar saat ini (menjual satu...menjual n, membeli satu...membeli n), dan mencatat data buku pesanan diself.orderBookSelanjutnya, menilai apakah pesanan pembelian dan pesanan penjualan data buku pesanan kurang dari 3, jika demikian, fungsi tidak valid akan dikembalikan langsung.

Setelah itu, dua data dihitung:

· Menghitung harga bill of lading Harga bill of lading juga dihitung dengan menggunakan metode rata-rata tertimbang. Ketika menghitung pesanan pembelian, bobot yang diberikan pada harga pembelian yang paling dekat dengan harga transaksi adalah 61,8% (0,618) dan bobot yang diberikan pada harga penjualan yang paling dekat dengan harga transaksi adalah 38,2% (0,382) Ketika menghitung harga bill of lading bill of sale, bobot yang sama diberikan kepada harga jual yang paling dekat dengan harga transaksi. Untuk mengapa adalah 0,618, mungkin penulis lebih memilih rasio bagian emas. Untuk harga terakhir (0,01), itu adalah untuk mengimbangi ke pusat pembukaan sedikit.

· Membaharui harga rata-rata tertimbang dari tiga tingkat pertama dari buku pesanan pada seri waktu Untuk tiga tingkat pertama harga pesanan pembelian dan penjualan dalam buku pesanan, rata-rata tertimbang dihitung. Berat tingkat pertama adalah 0,7, berat tingkat kedua adalah 0,2, dan berat tingkat ketiga adalah 0,1. Seseorang mungkin berkata, Oh, tidak, ada 0,7, 0,2, 0,1 dalam kode. Mari kita memperluas perhitungan:

(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 akhir sebenarnya merupakan respons terhadap posisi harga di tengah pembukaan ketiga di pasar saat ini. Kemudian gunakan harga yang dihitung ini untuk memperbarui arrayself.prices, menendang salah satu data tertua (melaluishift()Fungsi) dan memperbarui salah satu data terbaru ke dalamnya (melaluipush()fungsi, shift dan push fungsi adalah metode dari JS bahasa array objek, Anda dapat memeriksa data JS untuk rincian). Dengan demikian membentuk arrayself.prices, yang merupakan aliran data dengan urutan deret waktu.

Jadi mari kita beristirahat di sini, dan kita akan melihat Anda edisi berikutnya ~


Berkaitan

Lebih banyak