| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- 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()
|