Type/to search
8
Follow
1361
Followers
Analisis Strategi Pemanen Daun Bawang (1)
HFT
Created 2020-11-12 22:11:32  Updated 2024-12-06 22:20:54
 26
 11227

img

Analisis Strategi Pemanen Daun Bawang

Baru-baru ini, penemu diskusi grup WeChat kuantitatifprint moneyDiskusi tentang robot sangat panas, dan strategi yang sangat lama muncul kembali dalam visi para ahli kuantitatif:Pemanen daun bawang
print moneyPrinsip perdagangan robot ini mengacu pada strategi pemanen daun bawang. Saya menyalahkan diri sendiri karena tidak memahami strategi pemanen daun bawang dengan jelas saat itu. Jadi, saya meninjau kembali strategi aslinya dengan saksama, dan juga meninjau versi yang ditransplantasikan pada Inventor Quant.Transplantasi Pemanen Daun Bawang OKCoin
Mari kita ambil versi transplantasi dari strategi pemanen daun bawang dari Inventor Quantitative Platform untuk menganalisis strategi dan mengeksplorasi ide-ide di baliknya. Sehingga pengguna platform dapat mempelajari ide strategis ini.
Dalam artikel ini, kami akan menganalisis lebih lanjut dari perspektif pemikiran strategis, niat, dll., dan mencoba mengurangi konten membosankan terkait pemrograman.

[Transplantasi OKCoin Leek Harvester] Kode sumber strategi:

pine
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("开始平衡", 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("开始平衡", 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 suatu strategi untuk dipelajari, saat Anda membacanya, pertama-tama Anda harus melihat pada struktur program secara keseluruhan. Strategi ini tidak memiliki banyak kode, hanya kurang dari 200 baris kode, yang sangat ringkas, dan tingkat pemulihan ke strategi asli sangat tinggi, dan pada dasarnya sama. Kode kebijakan berjalan darimain()Fungsi mulai dijalankan, dan seluruh kode strategimain(), adalahLeeksReaper()Fungsi,LeeksReaper()Fungsinya juga mudah dipahami. Fungsi ini dapat dipahami sebagai konstruktor modul logika strategi pemanen daun bawang (sebuah objek). SederhananyaLeeksReaper()Bertanggung jawab untuk membangun logika transaksi pemanen daun bawang.

Kata kunci:
img
img

  • StrategimainBaris pertama fungsi:
    var reaper = LeeksReaper(), kode tersebut mendeklarasikan variabel lokalreaperKemudian panggil fungsi LeeksReaper() untuk membuat objek logika strategi dan menetapkannya kereaper

  • StrategimainFungsinya adalah sebagai berikut:

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

    Masukan sebuahwhileLoop mati, eksekusi tanpa akhirreaperFungsi pemrosesan objekpoll()poll()Fungsinya adalah logika utama dari strategi perdagangan, dan seluruh program strategi mulai menjalankan logika perdagangan secara terus-menerus.
    Adapun untukSleep(TickInterval)Baris ini mudah dipahami. Baris ini digunakan untuk mengontrol waktu jeda setelah setiap eksekusi logika transaksi secara keseluruhan, untuk mengontrol frekuensi rotasi logika transaksi.

Pembuatan profilLeeksReaper()Konstruktor

LihatlahLeeksReaper()Bagaimana fungsi membangun objek logika strategi.

LeeksReaper()Pada awal fungsi, objek kosong dideklarasikan.var self = {},adaLeeksReaper()Selama pelaksanaan fungsi, beberapa metode dan properti akan secara bertahap ditambahkan ke objek kosong ini, dan akhirnya konstruksi objek ini akan selesai, dan akhirnya objek ini akan dikembalikan (yaitu,main()Di dalam fungsivar reaper = LeeksReaper()Pada langkah ini, objek yang dikembalikan ditugaskan kereaper)。

MemberiselfMenambahkan properti ke suatu objek

Berikutnya berikanselfBanyak properti telah ditambahkan. Saya akan menjelaskan setiap properti di bawah ini sehingga Anda dapat dengan cepat memahami tujuan dan maksud dari properti dan variabel ini, yang akan memudahkan Anda memahami strategi ini dan menghindari kebingungan saat melihat tumpukan kode ini.

python
self.numTick = 0 # 用来记录poll函数调用时未触发交易的次数,当触发下单并且下单逻辑执行完时,self.numTick重置为0 self.lastTradeId = 0 # 交易市场已经成交的订单交易记录ID,这个变量记录市场当前最新的成交记录ID self.vol = 0 # 通过加权平均计算之后的市场每次考察时成交量参考(每次循环获取一次市场行情数据,可以理解为考察了行情一次) self.askPrice = 0 # 卖单提单价格,可以理解为策略通过计算后将要挂卖单的价格 self.bidPrice = 0 # 买单提单价格 self.orderBook = {Asks:[], Bids:[]} # 记录当前获取的订单薄数据,即深度数据(卖一...卖n,买一...买n) self.prices = [] # 一个数组,记录订单薄中前三档加权平均计算之后的时间序列上的价格,简单说就是每次储存计算得到的订单薄前三档加权平均价格,放在一个数组中,用于后续策略交易信号参考,所以该变量名是prices,复数形式,表示一组价格 self.tradeOrderId = 0 # 记录当前提单下单后的订单ID self.p = 0.5 # 仓位比重,币的价值正好占总资产价值的一半时,该值为0.5,即平衡状态 self.account = null # 记录账户资产数据,由GetAccount()函数返回数据 self.preCalc = 0 # 记录最近一次计算收益时的时间戳,单位毫秒,用于控制收益计算部分代码触发执行的频率 self.preNet = 0 # 记录当前收益数值

MemberiselfMetode Penambahan Objek

Setelah menambahkan atribut-atribut ini ke diri sendiri, mulailah untukselfMenambahkan metode ke suatu objek memungkinkan objek tersebut melakukan beberapa pekerjaan dan memiliki beberapa fungsi.

Fungsi pertama yang ditambahkan:

cpp
self.updateTrades = function() { var trades = _C(exchange.GetTrades) # 调用FMZ封装的接口GetTrades,获取当前最新的市场成交数据 if (self.prices.length == 0) { # 当self.prices.length == 0时,需要给self.prices数组填充数值,只有策略启动运行时才会触发 while (trades.length == 0) { # 如果近期市场上没有更新的成交记录,这个while循环会一直执行,直到有最新成交数据,更新trades变量 trades = trades.concat(_C(exchange.GetTrades)) # concat 是JS数组类型的一个方法,用来拼接两个数组,这里就是把“trades”数组和“_C(exchange.GetTrades)”返回的数组数据拼接成一个数组 } for (var i = 0; i < 15; i++) { # 给self.prices填充数据,填充15个最新成交价格 self.prices[i] = trades[trades.length - 1].Price } } self.vol = 0.7 * self.vol + 0.3 * _.reduce(trades, function(mem, trade) { # _.reduce 函数迭代计算,累计最新成交记录的成交量 // 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) }

updateTradesTujuan fungsi ini adalah untuk memperoleh data transaksi pasar terkini, melakukan beberapa kalkulasi berdasarkan data dan mencatatnya untuk digunakan dalam logika strategi selanjutnya.
Saya menulis komentar baris demi baris langsung dalam kode di atas.
untuk_.reduceSiswa yang tidak memiliki dasar pemrograman mungkin akan kebingungan. Berikut penjelasan singkatnya._.reduceYaUnderscore.jsFungsi pustaka ini didukung oleh strategi FMZJS, sehingga sangat mudah digunakan untuk perhitungan berulang.Tautan data Underscore.js

Artinya juga sangat sederhana, misalnya:

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

Yaitu, array[1, 2, 3, 4]Jumlahkan setiap angka dalam . Kembali ke strategi kita,tradesNilai volume transaksi dari setiap catatan transaksi dalam array dijumlahkan. Dapatkan catatan transaksi terkini dan total volume transaksi.self.vol = 0.7 * self.vol + 0.3 * _.reduce(...)Tolong ijinkan saya untuk menggunakannya...Daripada sekumpulan kode itu. Tidak sulit untuk melihat di sini bahwaself.volPerhitungan juga merupakan rata-rata tertimbang. Artinya, total volume transaksi terkini menyumbang 30% bobot, dan volume transaksi yang diperoleh dari perhitungan bobot sebelumnya menyumbang 70%. Rasio ini ditetapkan secara artifisial oleh pembuat strategi dan mungkin terkait dengan pengamatan pola pasar.
Mengenai pertanyaan Anda, bagaimana jika antarmuka untuk memperoleh data transaksi terkini mengembalikan data lama yang sama kepada saya? Maka data yang saya peroleh akan salah, jadi apakah ada gunanya menggunakannya? Jangan khawatir, masalah ini telah dipertimbangkan saat merancang strategi, jadi disertakan dalam kode.

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

Penghakiman ini. Hal ini dapat dinilai berdasarkan ID transaksi dalam catatan transaksi. Akumulasi hanya dipicu ketika ID lebih besar dari ID catatan terakhir, atau jika antarmuka pertukaran tidak memberikan ID, yaitu,trade.Id == 0, menggunakan stempel waktu dalam catatan transaksi untuk menentukan, pada saat iniself.lastTradeIdYang disimpan adalah stempel waktu catatan transaksi, bukan ID.

Fungsi tambahan kedua:

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

Tontonan berikutnyaupdateOrderBookSeperti namanya, fungsi ini digunakan untuk memperbarui buku pesanan. Namun, ini lebih dari sekadar memperbarui buku pesanan. Fungsi mulai memanggil fungsi API FMZGetDepth()Dapatkan data buku pesanan pasar saat ini (jual satu...jual n, beli satu...beli n), dan catat data buku pesanan diself.orderBooktengah. Berikutnya, jika data buku pesanan berisi kurang dari 3 pesanan beli dan jual, fungsi tersebut dianggap tidak valid dan langsung kembali.

Setelah itu, dua data dihitung:

  • Hitung harga bill of lading
    Harga bill of lading juga dihitung menggunakan rata-rata tertimbang. Saat menghitung order beli, order beli diberi bobot lebih besar sebesar 61,8% (0,618), dan order jual memperhitungkan bobot sisanya sebesar 38,2% (0,382).
    Hal yang sama berlaku saat menghitung harga jual bill of lading, dengan harga jual diberi bobot lebih besar. Adapun mengapa nilainya 0,618, mungkin karena penulis lebih menyukai rasio emas. Adapun kenaikan atau penurunan harga terakhir yang sedikit (0,01), tujuannya adalah untuk sedikit menggesernya ke arah pusat pasar.

  • Perbarui harga rata-rata tertimbang dari tiga tingkatan pertama buku pesanan pada deret waktu
    Perhitungan rata-rata tertimbang dilakukan pada tiga tingkatan pertama harga pesanan beli dan jual dalam buku pesanan, dengan tingkatan pertama memiliki bobot 0,7, tingkatan kedua memiliki bobot 0,2, dan tingkatan ketiga memiliki bobot 0,1. . Beberapa siswa mungkin berkata: “Oh, itu tidak benar, tidak ada 0,7, 0,2, 0,1 dalam kode tersebut”
    Mari kita perluas perhitungannya:

    (买一 + 卖一) * 0.35 + (买二 + 卖二) * 0.1 + (买三 + 卖三) * 0.05 -> (买一 + 卖一) / 2 * 2 * 0.35 + (买二 + 卖二) / 2 * 2 * 0.1 + (买三 + 卖三) / 2 * 2 * 0.05 -> (买一 + 卖一) / 2 * 0.7 + (买二 + 卖二) / 2 * 0.2 + (买三 + 卖三) / 2 * 0.1 -> 第一档平均的价格 * 0.7 + 第二档平均的价格 * 0.2 + 第三档平均的价格 * 0.1

    Di sini Anda dapat melihat bahwa harga akhir yang dihitung sebenarnya mencerminkan posisi harga tengah dari tiga level di pasar saat ini.
    Kemudian gunakan harga terhitung ini untuk memperbaruiself.pricesArray, tendang keluar data tertua (melaluishift()fungsi), memperbarui data terbaru (melaluipush()Fungsi Function, Shift, dan Push adalah metode objek array bahasa JS. Anda dapat memeriksa informasi JS untuk detailnya). Dengan demikian terbentuklahself.pricesArray adalah aliran data yang terurut menurut rentang waktu.

Ahem, minumlah air, aku akan berhenti di sini dulu, sampai jumpa lain waktu~

Related Recommendations
Comment
All comments (21)

    梦总你好,想问一下。self.prices 先填充的15个历史成交价格,然后又填充订单薄前三档加权平均价格. 对吧

    5 years ago

    是的。

    5 years ago

    我想给梦总点个赞

    5 years ago

    可惜了,很多交易所的做市商已经将盘口买一卖一价压到了只剩下一个tick,让策略里这种尝试插入到买一卖一中间的操作没有了意义

    6 years ago

    主要是学习思路,洞察这些可以高频的交易机会。

    6 years ago

    谢谢了,我写了python的版本在山寨币上跑了一下,真是手续费收割机啊。。。不到5分钟20刀没了:)

    6 years ago

    不客气,可以看下草神写的策略原理分析的文章,高频策略是需要一些支持的。

    6 years ago

    太棒了,没有梦总的解释还真的不能完全看懂,感谢大佬的耐心解释!

    6 years ago

    感谢支持~如果喜欢,帮忙分享下,嘿嘿~

    6 years ago

    黄金分割0.618 0.382是用的斐波
    梦总牛批

    6 years ago

    上学那会儿,学到这个黄金分割比例记得特别清楚,说是这样比例长宽的矩形最美~~但是不知道为什么。。。。

    6 years ago

    确实牛批啊这么详细

    6 years ago

    感谢支持!

    6 years ago

    大大的赞,这么详细的解答.感谢分享

    6 years ago

    感谢支持!

    6 years ago

    梦总牛p

    6 years ago

    感谢支持!

    6 years ago

    梦总,牛批!!
    虽然注释了,但是看起来还是非常复杂.....

    6 years ago

    其实不复杂,这次注释的比较啰嗦,逐行都尽量用最容易理解的方式描述了。通篇看下来应该理解不难。

    6 years ago

    梦总,牛批!!

    6 years ago

    感谢支持。

    6 years ago
  • 1
iPhone Download
Forums
PINE Language
© 2015 - ∞ INVENTOR PTE LTD (SG)