Baru-baru ini, terdapat perbincangan hangat mengenaiprint money
Satu strategi lama telah memasuki mata Quant lagi: LeeksReaper.
Prinsip perdagangan robotprint money
Oleh 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)
}
}
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:
· Barisan pertama strategimain
fungsi:var reaper = LeeksReaper()
, kod menyatakan pembolehubah tempatanreaper
dan kemudian memanggil fungsi LeeksReaper() untuk membina objek logik strategi yang menetapkan nilai kepadareaper
.
Langkah strategi seterusnyamain
fungsi:
while (true) {
reaper.poll()
Sleep(TickInterval)
}
Masukkanwhile
gelung tanpa henti dan terus melaksanakan fungsi pemprosesanpoll()
daripadareaper
objek,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.
LeeksReaper()
pembinaLihat 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 kepadaself
objek
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
Selepas menambah sifat-sifat ini kepada diri sendiri, mula menambah kaedah untukself
objek 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)
}
FungsiupdateTrades
adalah 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._.reduce
adalah 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 dalamtrades
Dapatkan 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.vol
adalah 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.lastTradeId
menyimpan 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,
(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 ~