Baru-baru ini, ada perdebatan hangat tentangprint money
Sebuah strategi yang sangat lama telah masuk ke mata Quants lagi: LeeksReaper.
Prinsip perdagangan robotprint money
Jadi, 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)
}
}
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:
· Jalur pertama dari strategimain
Fungsi:var reaper = LeeksReaper()
, kode menyatakan variabel lokalreaper
dan kemudian memanggil fungsi LeeksReaper() untuk membangun objek logika strategi yang menetapkan nilai untukreaper
.
Langkah berikutnya dari strategimain
Fungsi:
while (true) {
reaper.poll()
Sleep(TickInterval)
}
Masukkan awhile
loop tanpa akhir dan terus menjalankan fungsi pemrosesanpoll()
darireaper
objek, 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.
LeeksReaper()
konstruktorLihatlah 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 keself
objek
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
Setelah menambahkan atribut ini untuk diri sendiri, mulai menambahkan metode untukself
objek 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)
}
FungsiupdateTrades
adalah 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._.reduce
adalah 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 ditrades
Dapatkan total volume transaksi terbaruself.vol = 0.7 * self.vol + 0.3 * _.reduce (...)
, di sini kita menggunakan...
Tidak sulit untuk melihat perhitungan dariself.vol
juga 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.lastTradeId
menyimpan 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.orderBook
Selanjutnya, 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,
(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 ~