前几期文章我们分析了原版的现货版韭菜收割机高频策略的思路以及代码实现。
对于币圈量化的很多用户都比较关注print money大佬的策略,print money大佬的策略是在币安USDT合约交易的。从观察以及众多关注者的分析可知,该高频策略类似韭菜收割机的原理(草神也说过高频策略原理比较趋近)。但是肯定有精妙之处能实现策略有一个稳定的胜率和适当的盈亏比。
所以技痒的小编也忍不住魔改了一把,虽说魔改过的策略效果被大神们的策略碾压至渣渣。但是也算是对于高频策略的学习实践了,有兴趣的FMZer同学一起来探讨、学习下吧。
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)
}
}
策略是计划使用在币安USDT合约市场交易,币安合约支持单向持仓。所以策略就按照单向持仓的特性修改设计(单向持仓更加方便策略修改),不考虑平仓,只考虑买卖。这样思路也比较贴近现货版的韭菜收割机。
策略基本保留了原版的短期价格趋势突破判定标准,短期价格突破幅度由参数burstThresholdPct
控制,根据这个判定条件来判断短期价格为bull
(牛),还是bear
(熊)。
策略剔除了原版一些模块,比如平衡模块。较大的改动是把下单改为了在订单薄中挂单,等待成交。 期望在多空博弈激烈的混乱盘口中用较低的成本开仓,追随短期趋势,并且在短期趋势反转时平仓,继续反向挂单开仓。
策略删除了其它没用的代码所以非常简短,也很简单。虽然策略是个不赚钱的策略,甚至亏钱,但是作为FMZer学习高频策略,观察高频策略的行为、观察市场微观规律等是一个可以上手的模型。程序化交易、量化交易需要通过大量的实践、经验、理论作为基础。
可以看到,行情不活跃的时候开平仓是比较困难的。
目前,还没有找到好的优化方向。 有兴趣的同学请踊跃发言,一起探讨。
策略地址:https://www.fmz.com/strategy/260806
本策略仅仅用于学习,行情平淡实盘可能亏损。
烤韭菜 这里合约怎么设置杠杆倍数呢?
🏆Benson 梦总,你的策略在另一个平台出现了,请注意版权意识
gillbates2 当单边成交的时候,应该怎么处理啊。。。
轨迹生物 这个能不能加一个交易量的判定,或者自动选币的判定。挂单这个挺好的
qslll 有跑起来的吗 能分享一下数据不
量化卜泰邢 updateOrderBook 中计算price那里,原版加权之后price价格应该在盘口买n卖n的中位数附近,本文里面加权计算price之后是2倍(买卖盘口中位数附近),这里不是很懂。。
168 梦神能调一个可回测的,可用于现货的版本吗?多谢多谢
168 main:68:43 - TypeError: Cannot read property 'totalWalletBalance' of undefined 请问小梦,这个不能回测,必须上实盘么?如何调整?先谢了!
bengban 实盘跑一会的图片太皮了
sadfkj8i 合约的trades好像无法获取到,策略一直跑不起来,大佬是怎么跑起来的。
发明者量化-小小梦 可以挂单档位降低一些,但是有利有弊吧。这个高频策略需要盘面有好的深度、多空博弈激烈的场景。
烤韭菜 梦总,我测下这个策略,这个挂单100ms内没有成交就会被取消,我跑了一晚上(差不多5个小时吧)总共没下单且成交过5单,这要怎么修改参数吗?
发明者量化-小小梦 直接在交易所设置杠杆就可以了。
发明者量化-小小梦 哈哈 ,好的,感谢提醒。
发明者量化-小小梦 这个策略是教学策略,主要看下思路,仅仅是教学。
m0606 我也觉得限价单这个挺好……
发明者量化-小小梦 INFO 是麦语言吧。支持的
路过 回测不支持INFO信息
bengban tick限价单设计贼好
发明者量化-小小梦 !>_>! 这个只是个原型,亏钱的,学习用,只是有个研究对象而已。