量化交易Demo

OKCoin韭菜收割机

原文地址:https://www.botvs.com/strategy/34388

为了学习方便,在自己理解的基础上加上了中文注释。

文件地址:okcoin韭菜收割机(注释).js

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.prices中,交易量self.vol
    self.updateTrades = function() {
        // _C,重试函数, 会一直调用指定函数到成功返回,这里就是获取交易所交易信息
        // 获取交易所交易历史,历史成交订单
        var trades = _C(exchange.GetTrades)
        // length:将参数的数量返回回来,在这里就是将prices数组中存的个数返回回来。
        if (self.prices.length == 0) {
            while (trades.length == 0) {
                // concat()方法能将两个数组合并,并不改变原来的数组,返回一个新数组
                trades = trades.concat(_C(exchange.GetTrades))
            }
            for (var i = 0; i < 15; i++) {
                // 将trades数组中最后一个的Price价格信息存储到prices数组中
                self.prices[i] = trades[trades.length - 1].Price
            }
        }
        // reduce
        // function(mem, trade) 做两件事情。
        // 1:将trades中的Amount累加起来,保存在mem中返回回来。
        // 2:取trades中的最大Id保存在Id
        self.vol = 0.7 * self.vol + 0.3 * _.reduce(trades, function(mem, trade) {
            // Huobi not support trade.Id

            // self.lastTradeId 存储最大交易的ID
            // 如果trade.Id > self.lastTradeId
            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.bidPrice,self.askPrice,根据买单,卖单价格计算出一个价格填入到self.prices中
    self.updateOrderBook = function() {
        // 获取交易所订单薄
        var orderBook = _C(exchange.GetDepth)
        self.orderBook = orderBook
        // bid 是出价的意思,是卖单。 ask是买单
        // 如果买单或卖单单子不足3比则取消。意思就是深度不够停止交易
        if (orderBook.Bids.length < 3 || orderBook.Asks.length < 3) {
            return
        }
        //设置卖单价格,最低的卖单价格*0.618+最高的买单价格*0.382 算出来的价格会比最低的卖单价格低
        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


        // shift()移除数组中的第一个元素,并将移除的这个元素返回。
        self.prices.shift()
        // push,在数组末尾添加一个元素,并将数组的大小返回
        // _N 控制小数点精度。
        // 根据买单,卖单计算出一个价格,存在self.prices中
        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()
        // 卖单数量大于0 并且 当前时间与上次平衡仓位间隔超过的固定的时间
        if (self.orderBook.Bids.length > 0 && now - self.preCalc > (CalcNetInterval * 1000)) {
            // 当前时间更新给self.preCalc
            self.preCalc = now
            // Balance  余额(定价货币余额, ETH_BTC的话BTC为定价货币)
            // FrozenBalance   冻结的余额
            // Stocks  交易货币的可用数量, 数字货币现货为当前可操作币的余额(去掉冻结的币), 数字货币期货的话为合约当前可用保证金(传统期货无此属性) 。
            // FrozenStocks    冻结的交易货币的可用数量(传统期货无此属性)
            // 账号里面所有资产转换成以BTC计价,计算出的总资产,如果是ETH_BTC交易对就用,BTC计价
            var net = _N(account.Balance + account.FrozenBalance + self.orderBook.Bids[0].Price * (account.Stocks + account.FrozenStocks))
            // 如果算出来的新的资产总数,不等于self.preNet,将net更新到self.preNet
            if (net != self.preNet) {
                self.preNet = net
                LogProfit(net)
            }
        }
        // 
        self.btc = account.Stocks
        self.cny = account.Balance
        // 如果是ETH_BTC交易对,那么BTC为定价货币,self.p计算的就是手中ETH占总资产的比例
        self.p = self.btc * self.prices[self.prices.length-1] / (self.btc * self.prices[self.prices.length-1] + self.cny)
        var balanced = false
        
        // 比例低于48%,开始买入
        if (self.p < 0.48) {
            Log("开始平衡", self.p)
            self.cny -= 300
            if (self.orderBook.Bids.length >0) {
                // exchange.Buy(Price, Amount), price为订单价格,Amount为订单数量
                // 如果是ETH_BTC交易对,exchange.Buy(1, 0.1),用1CNY买入0.1个BTC
                // exchange.Buy(-1, 0.1)    // 下市价单 买入,买入 0.1 个 BTC。
                // 比最低的卖单价格高一点,买入0.01个BTC
                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) {
            // 比例低高于52%,开始卖出
            Log("开始平衡", self.p)
            self.btc -= 0.03
            if (self.orderBook.Asks.length >0) {
                // 如果是ETH_BTC交易对,exchange.Sell(1, 1),用1ETH买入0.1个BTC
                // exchange.Sell(-1, 0.2)   // 注意: 下市价单 卖出,卖出 0.2 个 BTC
                // 比最高的买单价低卖出
                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 休眠函数,使程序暂停一段时间
        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]取数组中最后一个价格数据
            // self.prices.slice(-6, -1) 取self.prices数组中倒数第6个数
            // self.prices.slice(-6, -2) 取self.prices数组中倒数第6,7个数,两个数
            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
        }

        // 如果是牛,交易价格是:self.bidPrice 
        var tradePrice = bull ? self.bidPrice : self.askPrice
        while (tradeAmount >= MinStock) {
            // 如果牛执行买单,如果熊执行卖单
            var orderId = bull ? exchange.Buy(self.bidPrice, tradeAmount) : exchange.Sell(self.askPrice, tradeAmount)
            // 参数为毫秒数,如Sleep(1000)为休眠一秒
            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
                // 减去成交数量,order.DealAmount:成交数量
                tradeAmount -= order.DealAmount
                // 减少交易量
                tradeAmount *= 0.9
                if (order.Status == ORDER_STATE_CANCELED) {
                    // 更新买单,卖单簿        
                    self.updateOrderBook()
                    // 下面的两个while,目的是确保在牛市时,我的出价比最低的卖价低0.1,在熊市时,确保我的
                    // 如果是是牛市,并且最低卖单价格-交易价格>0.1
                    while (bull && self.bidPrice - tradePrice > 0.1) {
                        // 减少交易数量*0.99
                        tradeAmount *= 0.99
                        // 因为最低的卖单价格比交易价格高,并且高出的金额大于0.1,所以我的交易价格+0.1,
                        tradePrice += 0.1
                    }
                    // 如果是熊市,并且卖单的价格比我的交易价格低,我就要降价,降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)
    }
}

你可能感兴趣的:(量化交易Demo)