Transplantation from:https://github.com/richox/okcoin-leeks-reaper
The original author said that it would not work after the fee, so I just did the transplant, no physical testing, interested in learning. Inventors of Quantitative Tick-level retrieval support playback of Depth and Trades, allowing direct retrieval of learning strategy logic
The following is the original description
This is a high-frequency trading robot program on the OKCoin Bitcoin trading platform, which was successfully brushed from its initial investment of 6,000 to 250,000 by mid-January 2017. The strategy has effectively failed due to the recent high-pressure policy of the Central Bank against Bitcoin, which has led to the cessation of all major platforms and the start of charging transaction fees.
This robotic program is based on two main strategies:
This procedure requires balancing the position, i.e. (capital + financing = money), so that the position is at 50% when the net assets do not fluctuate with the price, but also guarantees when trend fluctuations occur.It's all right.。
Thank you to the following two projects:
Thanks to OKCoin:
BTC: 3QFn1qfZMhMQ4FhgENR7fha3T8ZVw1bEeU
/*backtest start: 2019-09-05 00:00:00 end: 2019-09-05 22:00:00 period: 1h exchanges: [{"eid":"Binance","currency":"BTC_USDT","fee":[0,0]}] mode: 1 */ 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) } }
The King of Hip HopThe strategy has failed, and profits come from high-frequency low-differential income with zero handling fees.
kongbai979 self.vol到底是个啥?是一个ticker期间内所有交易量的总和吗?
dayTrader2018for (var i = 0; i < 15; i++) { self.prices[i] = trades[trades.length - 1].Price }; is there a bit of a problem here, so isn't every element in the price array the most recent transaction price?
kouyou7035I have several exchanges with no fees and good trading depths. Can you try this strategy?
bijiasuoIt's great, I'm sorry it won't work, is it working now?
skyfffireThe price of the commentary is: 2000 copies, unofficial, with intentional link.
rajajackIf anyone has tried it, come out and discuss the benefits.
valennnWhen will the annotations come?
wuqianmingHow do you run a botvs unsupported, free-to-trade exchange, how do you write an exchange API?
JThank you for sharing such a great strategy! exchange.CancelOrder ((orders[i].Id) The code for cancellation has a bit of a problem with the constant cancellation of orders. If you look at the original code below, you should have waited 10 seconds to withdraw it. The rest is basically fine, and I've changed it to run on a fee-free exchange, which feels good.
kmstudio 66行prices需加.length
snowpeaWhat language is that?
Zero 平衡那里下单忘加上Price属性了。。已更正,有发现bug请及时提交.
The AquariusIt seems that there is a strategy to make money, or not to sell and keep your money.
1213761768It's too early.
Chang is unyielding.No, the bid price has to be less than the ask price, one is the bid price, one is the sell price.
bwxiaokPlease, when calculating bid price and ask price, the order is placed as if the bid price is not greater than the ask price, so if the buy and sell is very close, the calculation is very likely to be high buy low sell, will there be this problem?
I love the fluff.I wrote a commentary that everyone needs to be able to add my Weibo post Hoo_tongxue, and most importantly, it's free.
I love the fluff.The real face is thick enough.
Small coal ballsWhat exchanges?
ZeroThis should initialize to the latest price, followed by a shift operation.
Miracle at the Re-StorageIt's been a long time since we've had a robot war.
suskiYes, this is a better cut of cabbage ^_^
skyfffireLet the seller sell, and let the buyer buy; and let them not contend with thee.
The bride too.The source code has been made public by everyone, can you comment on it, sell 2000?
akkk 请问zaif.jp现在还是免手续费吗?怎么通过认证呢
xiahaohuan001See the ID.
J 在Zaif.jp上测试,买一卖一间经常就没有空间,用这个策略有什么问题吗?
JIs it true? Come out and give a lecture.
xiahaohuan001Surprised by Author
JGood, I have a few more questions, please, and another post: https://www.botvs.com/bbs-topic/677
ZeroIf you have any questions or lessons to share about this strategy, please post them in the forum, thanks for your support!
ZeroThanks for the suggestion, I added a parameter, balance order waiting time, if you want to process simultaneously, you can record the ID of each order with the time of ordering, and then selectively cancel, so as to reduce the delay, but logically, should be first balance re-opening, because not all exchanges support the Time attribute to get the order, so the platform does not add this attribute in the Order, needing its own statistics.
xiahaohuan001I feel like it's a weird strategy, I like it.
ZeroI haven't tried this, so I need you to do your own research.
Orion1708Hard Z major port. Please, does changing the policy parameter default have a big impact on the policy?
ZeroHa ha, thank you, it's been added.
Zero Javascript
ZeroHigh frequency of transactions
Inventors quantify - small dreamsThe original author was also released on the same day as the fee was collected.
xiahaohuan001Why are trend strategists afraid of shock costs?