In the previous articles, we analyzed the ideas and code implementation of the original spot version of the high-frequency profit harvester strategy.
Many users in the quantitative cryptocurrency circle are very concerned about the strategy developed by a master called print money. The strategy of print money is traded in the Binance USDT contract. From the observation and the analysis of many followers, it can be seen that the high-frequency strategy is similar to the principle of the profit harvester (master Xiaocao also said that the principle of the high-frequency strategy is similar to the profit harvester). But there must be some subtlety that enables a strategy to have a stable winning rate and an appropriate profit-to-loss ratio.
Therefore, I was so excited that I couldn’t help but wanted to magically modify the strategy, even the magically modified strategy result and effect was nothing in front of the strategies developed by masters. But it is also a learning practice for high-frequency strategy. Interested FMZers can discuss and learn about it together.
var TickInterval = 100
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.account = null
self.buyPrice = 0
self.sellPrice = 0
self.state = 0
self.depth = null
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.depth = orderBook
self.buyPrice = orderBook.Bids[pendingLevel].Price
self.sellPrice = orderBook.Asks[pendingLevel].Price
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.15 +
(orderBook.Bids[1].Price + orderBook.Asks[1].Price) * 0.1 +
(orderBook.Bids[2].Price + orderBook.Asks[2].Price) * 0.1 +
(orderBook.Bids[3].Price + orderBook.Asks[3].Price) * 0.075 +
(orderBook.Bids[4].Price + orderBook.Asks[4].Price) * 0.05 +
(orderBook.Bids[5].Price + orderBook.Asks[5].Price) * 0.025))
}
self.updateAccount = function() {
var account = exchange.GetAccount()
if (!account) {
return
}
self.account = account
LogProfit(parseFloat(account.Info.totalWalletBalance), account)
}
self.CancelAll = function() {
while (1) {
var orders = _C(exchange.GetOrders)
if (orders.length == 0) {
break
}
for (var i = 0; i < orders.length; i++) {
exchange.CancelOrder(orders[i].Id)
}
Sleep(100)
}
}
self.poll = function() {
self.numTick++
self.updateTrades()
self.updateOrderBook()
var pos = _C(exchange.GetPosition)
var burstPrice = self.prices[self.prices.length - 1] * burstThresholdPct
var bull = false
var bear = false
LogStatus(_D(), "\n", 'Tick:', self.numTick, 'self.vol:', self.vol, ', 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
} 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
}
if (pos.length != 0) {
if (pos[0].Type == PD_LONG) {
self.state = 1
} else {
self.state = 2
}
} else {
self.state = 0
}
if ((!bull && !bear)) {
return
}
if (bull) {
var price = (self.state == 0 || self.state == 1) ? self.buyPrice : self.depth.Bids[coverPendingLevel].Price
var amount = (self.state == 0 || self.state == 1) ? pendingAmount : pos[0].Amount
exchange.SetDirection("buy")
exchange.Buy(price, amount)
} else if (bear) {
var price = (self.state == 0 || self.state == 2) ? self.sellPrice : self.depth.Asks[coverPendingLevel].Price
var amount = (self.state == 0 || self.state == 2) ? pendingAmount : pos[0].Amount
exchange.SetDirection("sell")
exchange.Sell(price, amount)
}
self.numTick = 0
Sleep(TickInterval)
self.CancelAll()
self.updateAccount()
}
while (!self.account) {
self.updateAccount()
Sleep(500)
}
Log("self.account:", self.account)
return self
}
function main() {
LogProfitReset()
exchange.SetPrecision(pricePrecision, amountPrecision)
exchange.SetContractType("swap")
var reaper = LeeksReaper()
while (true) {
reaper.poll()
Sleep(100)
}
}
The strategy is planned to trade on the Binance USDT contract market, which supports one-way positions. Therefore, the strategy is modified and designed according to the characteristics of one-way positions (one-way positions are more convenient for strategy modification), and you only need to consider buying and selling, no need to think of closing positions. This way of thinking is also closer to the spot version of profit harvester.
The strategy basically retains the original short-term price trend breakout criterion, and the short-term price breakout range is controlled by the parameter burstThresholdPct
. According to the criterion to determine whether the short-term price is bull
or bear
.
The strategy removes some modules from the original, such as the balance module. A pretty big modification is to change placing orders to pending orders in the order book and waiting for execution. It is expected to open positions at a comparatively low cost in the chaotic market with fierce long-short game, follow the short-term trend, and close positions when the short-term trend reverses, and then continue to reversely pend orders and open positions.
The strategy removes other useless code, so it’s very short and simple. Although the strategy is an unprofitable strategy, even with losses, it is a very easy and useful model for an FMZer to learn high-frequency strategies, observe the actions of high-frequency strategies, observe the microscopic rules of the market, etc. Programmed and quantitative trading need to be based on a lot of practice, experience and theories.
It can be seen that it is more difficult to open and close positions when the market situation is not active.
At present, no good optimization direction has been found. Students, who are interested, can speak up actively and discuss it together.
Strategy address: https://www.fmz.com/strategy/260806
The strategy is only for study; when the market is flat, running it in the bot might make losses.