const OneTask = require('../libs/one-task') const OneInch = require('../libs/web3/1inch') const BinanceSpot = require('../libs/binance/binance-spot') const Config = require('../config/config') const NumKit = require('../kit/num-kit') const TimeKit = require('../kit/time-kit') const TableKit = require('../kit/table-kit') const BinanceKit = require("../libs/binance/binance-kit"); const orderHandler = async function(context, task) { const fileLogger = task.fileLogger const tokenMap = context.tokenMap const binanceSpot = context.binanceSpot const accountAssetMap = context.accountAssetMap // 逐对处理交易信息 for (const tokenHash of Object.keys(tokenMap)) { const token = tokenMap[tokenHash] const pair = token.exchange.pair // 价格非法判定 if (!token.BinancePrice || !token.OneInchPrice) continue; // 更新token的实际余额 token.orderAmount = NumKit.getSubFloat(accountAssetMap[token.exchange.symbol], token.exchange.lotSize) // 卖出逻辑判定 if (token.orderPrice && token.orderAmount > Math.pow(10, -token.exchange.lotSize)) { if (token.BinancePrice > token.orderPrice) { // 止盈逻辑 const isStopWin = token.BinancePrice > (token.orderPrice * (100 + Config.stopWinPercentage) / 100) if (isStopWin) { const sellRst = await binanceSpot.sell(pair, -1, token.orderAmount) if (sellRst.executedQty) { const cummulativeQuoteQty = NumKit.getSubFloat(sellRst.cummulativeQuoteQty, 6) const executedQty = NumKit.getSubFloat(sellRst.executedQty, token.exchange.lotSize) const price = cummulativeQuoteQty / executedQty fileLogger.info(`[止盈]${pair}, price: ${token.orderPrice}->${price}, amount: ${token.orderAmount}.`) token.orderPrice = undefined token.nextHandleTime = new Date().getTime() + Config.stopWinHandleSpaceHours * (60 * 60 * 1000) } else { token.orderPrice = undefined token.nextHandleTime = new Date().getTime() + Config.stopWinHandleSpaceHours * (60 * 60 * 1000) fileLogger.error(`[止盈失败]${pair}, ${JSON.stringify(sellRst)}`) } } } else { // 止损逻辑 const isStopLoss = token.BinancePrice < (token.orderPrice * (100 - Config.stopLossPercentage) / 100) if (isStopLoss) { const sellRst = await binanceSpot.sell(pair, -1, token.orderAmount) if (sellRst.executedQty) { const cummulativeQuoteQty = NumKit.getSubFloat(sellRst.cummulativeQuoteQty, 6) const executedQty = NumKit.getSubFloat(sellRst.executedQty, token.exchange.lotSize) const price = cummulativeQuoteQty / executedQty fileLogger.info(`[止损]${pair}, price: ${token.orderPrice}->${price}, amount: ${token.orderAmount}.`) token.orderPrice = undefined token.nextHandleTime = new Date().getTime() + Config.stopLossHandleSpaceHours * (60 * 60 * 1000) } else { token.orderPrice = undefined token.nextHandleTime = new Date().getTime() + Config.stopLossHandleSpaceHours * (60 * 60 * 1000) fileLogger.error(`[止损失败]${pair}, ${JSON.stringify(sellRst)}`) } } } } else if (token.isLong) { // 1. 获取base资产数量 const baseAssetAmount = accountAssetMap[Config.baseIerc20Token.symbol] // 判断余额是否够下单 if (Config.baseTokenAmount > baseAssetAmount) { fileLogger.info(`[下单失败]${pair}, volume: ${Config.baseTokenAmount}, asset: ${baseAssetAmount}.`) token.nextHandleTime = new Date().getTime() + Config.stopLossHandleSpaceHours * (60 * 60 * 1000) continue; } // 2. 下单获取成交数量以及平均价格 const buyRst = await binanceSpot.buy(pair, -1, Config.baseTokenAmount) if (buyRst.executedQty) { const cummulativeQuoteQty = NumKit.getSubFloat(buyRst.cummulativeQuoteQty, 6) // 计算扣除手续费后的实际成交数量 let executedQty = 0 for (const fill of orderRst.fills) { executedQty += parseFloat(fill.qty) // 扣除支付的手续费 if (fill.commissionAsset === 'HOOK') { executedQty -= parseFloat(fill.commission) } } executedQty = NumKit.getSubFloat(executedQty, token.exchange.lotSize) token.orderAmount = executedQty token.orderPrice = cummulativeQuoteQty / executedQty fileLogger.info(`[订单]${pair}, volume:${cummulativeQuoteQty}, 买入${executedQty}, 均价${token.orderPrice}.`) accountAssetMap[Config.baseIerc20Token.symbol] = accountAssetMap[Config.baseIerc20Token.symbol] - cummulativeQuoteQty } else { fileLogger.error(`[下单失败]${JSON.stringify(buyRst)}`) } } } } const table = new TableKit(['pair', '1inch', 'binance', 'order price', 'profit(%)', 'next time'], 20) const showInfo = function(context, task) { const logger = task.logger const tokenMap = context.tokenMap const accountAssetMap = context.accountAssetMap table.printTitles() Object.keys(tokenMap).forEach((tokenHash) => { const token = tokenMap[tokenHash] const pair = token.exchange.pair const OneInchPrice = token.OneInchPrice ? token.OneInchPrice : NaN const BinancePrice = token.BinancePrice ? token.BinancePrice : NaN const DiffPrice = token.DiffPrice const percentage = NumKit.getSubFloat((DiffPrice / OneInchPrice) * 100, 2) const orderPrice = token.orderPrice ? token.orderPrice : '' const orderPercentage = orderPrice ? NumKit.getSubFloat(((BinancePrice - orderPrice) / orderPrice) * 100, 2) : '' const nextTimeStr = token.nextHandleTime ? TimeKit.getTimeByMillisecond(token.nextHandleTime) : '' // 价格非法判定 const priceCondition = (() => { return OneInchPrice && BinancePrice })() const rows = [pair.toString(), OneInchPrice.toString(), BinancePrice.toString(), orderPrice.toString(), orderPercentage.toString(), nextTimeStr] // 识别到在拉盘 token.isLong = (() => { const percentageCondition = percentage > Config.percentageLimit const isNoPrevHandleTime = !token.nextHandleTime const isTimeCover = new Date().getTime() > token.nextHandleTime const timeCondition = isNoPrevHandleTime || isTimeCover return priceCondition && percentageCondition && timeCondition })() // 打印表格 table.showLine(rows, undefined) }) table.printEndLine(logger) logger.info(`base asset amount: ${accountAssetMap[Config.baseIerc20Token.symbol]}.`) logger.info('') } const priceHandler = async function(context, task) { const tokenMap = context.tokenMap const tokenContractAddressList = Object.keys(tokenMap) for (const tokenHash of tokenContractAddressList) { const toToken = tokenMap[tokenHash] const priceTick = toToken.exchange.priceTick const fromIerc20Token = Config.baseIerc20Token const amount = Config.baseTokenAmount const OneInchPrice = NumKit.getSubFloat(await OneInch.price(fromIerc20Token.contract, tokenHash, amount), priceTick) const BinancePrice = NumKit.getSubFloat(await BinanceSpot.realPrice(toToken.exchange.pair), priceTick) toToken.OneInchPrice = OneInchPrice toToken.BinancePrice = BinancePrice toToken.DiffPrice = BinancePrice - OneInchPrice if ((() => { return Math.abs(toToken.DiffPrice) < Math.pow(10, -priceTick) })()) { toToken.DiffPrice = 0 } { toToken.DiffPrice = NumKit.getSubFloat(toToken.DiffPrice, priceTick) } } } const accountHandler = async function(context, task) { const accountInfoRst = await context.binanceSpot.accountInfo() context.accountAssetMap = BinanceKit.parseBalancesToAccountAssetMap(accountInfoRst.balances) } const onTickFun = async function() { const task = this const context = task.context // 搜集所有价格数据 await priceHandler(context, task) // 获取账户数据 await accountHandler(context, task) // 展示关键信息 showInfo(context, task) // 交易、订单处理 await orderHandler(context, task) } const onePro = new OneTask('OnePro', Config.delay, OneTask.baseInit, onTickFun) onePro.Start()