one-pro.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. const OneTask = require('../libs/one-task')
  2. const OneInch = require('../libs/web3/1inch')
  3. const BinanceSpot = require('../libs/binance/binance-spot')
  4. const Config = require('../config/config')
  5. const PrivateConfig = require('../PrivateConfig')
  6. const NumKit = require('../kit/num-kit')
  7. const TimeKit = require('../kit/time-kit')
  8. const TableKit = require('../kit/table-kit')
  9. const BinanceKit = require("../libs/binance/binance-kit");
  10. const assert = require("assert");
  11. const holdingHandler = async function(context, task, token, pair) {
  12. const fileLogger = task.fileLogger
  13. const binanceSpot = context.binanceSpot
  14. const accountAssetMap = context.accountAssetMap
  15. // 这两个逻辑只有可能满足一个
  16. let isStopWin = token.BinancePrice > (token.orderPrice * (100 + PrivateConfig.stopWinPercentage) / 100)
  17. let isStopLoss = token.BinancePrice < (token.orderPrice * (100 - PrivateConfig.stopLossPercentage) / 100)
  18. assert.notEqual(isStopWin && isStopLoss, true, '止盈止损逻辑错误,请认真检查。')
  19. if (isStopWin || isStopLoss) {
  20. const orderPrice = token.orderPrice
  21. const orderAmount = token.orderAmount
  22. token.orderPrice = undefined
  23. token.nextHandleTime = new Date().getTime() + PrivateConfig.stopLossHandleSpaceHours * (60 * 60 * 1000)
  24. try {
  25. const sellMarketRst = await binanceSpot.sellMarket(pair, orderAmount)
  26. const price = NumKit.getSubFloat(sellMarketRst.avgPrice, token.exchange.priceTick)
  27. const cummulativeQuoteQty = NumKit.getSubFloat(sellMarketRst.cummulativeQuoteQty, 6)
  28. fileLogger.info(`[${isStopWin ? '盈+' : '损-'}]${pair}, `
  29. .concat(`成交额${cummulativeQuoteQty}, 均价${orderPrice}->${price}, 卖出${orderAmount}个.`)
  30. )
  31. } catch (e) {
  32. fileLogger.error(`[止${isStopWin ? '盈' : '损'}失败]${pair}, ${e}`)
  33. }
  34. }
  35. }
  36. const noHoldingHandler = async function(context, task, token, pair) {
  37. const fileLogger = task.fileLogger
  38. const binanceSpot = context.binanceSpot
  39. const accountAssetMap = context.accountAssetMap
  40. // 1. 获取base资产数量
  41. const baseAssetAmount = accountAssetMap[Config.baseIerc20Token.symbol]
  42. // 2. 判断余额是否够下单
  43. if (PrivateConfig.baseTokenAmount > baseAssetAmount) {
  44. fileLogger.info(`[余额不足]${pair}, 需要: ${PrivateConfig.baseTokenAmount}, 剩余: ${baseAssetAmount}.`)
  45. token.nextHandleTime = new Date().getTime() + PrivateConfig.stopLossHandleSpaceHours * (60 * 60 * 1000)
  46. return
  47. }
  48. // 3. 下单获取成交信息
  49. try {
  50. const buyMarketRst = await binanceSpot.buyMarket(pair, PrivateConfig.baseTokenAmount)
  51. token.cummulativeQuoteQty = NumKit.getSubFloat(buyMarketRst.cummulativeQuoteQty, 6)
  52. token.orderAmount = NumKit.getSubFloat(buyMarketRst.executedQty, token.exchange.lotSize)
  53. token.orderPrice = NumKit.getSubFloat(buyMarketRst.avgPrice, token.exchange.priceTick)
  54. fileLogger.info(`[单]${pair}, 成交额${token.cummulativeQuoteQty}, 买入${token.orderAmount}个, 均价${token.orderPrice}.`)
  55. accountAssetMap[Config.baseIerc20Token.symbol] -= token.cummulativeQuoteQty
  56. } catch (e) {
  57. fileLogger.error(`[下单失败]${pair}, ${e}`)
  58. }
  59. }
  60. const tradeLogic = async function(context, task) {
  61. const tokenMap = context.tokenMap
  62. // 逐对处理交易信息
  63. for (const tokenHash of Object.keys(tokenMap)) {
  64. const token = tokenMap[tokenHash]
  65. const pair = token.exchange.pair
  66. // 价格非法判定
  67. if (!token.BinancePrice || !token.OneInchPrice) continue;
  68. // 更新token的实际余额
  69. // token.orderAmount = NumKit.getSubFloat(accountAssetMap[token.exchange.symbol], token.exchange.lotSize)
  70. // 卖出逻辑判定
  71. if (token.orderPrice && token.orderAmount > Math.pow(10, -token.exchange.lotSize)) {
  72. await holdingHandler(context, task, token, pair)
  73. } else if (token.isLong) {
  74. await noHoldingHandler(context, task, token, pair)
  75. }
  76. }
  77. }
  78. const table = new TableKit(['pair', '1inch', 'binance', 'order price', 'profit(%)', 'next time'], 20)
  79. const showInfo = function(context, task) {
  80. const logger = task.logger
  81. const tokenMap = context.tokenMap
  82. const accountAssetMap = context.accountAssetMap
  83. table.printTitles()
  84. Object.keys(tokenMap).forEach((tokenHash) => {
  85. const token = tokenMap[tokenHash]
  86. const pair = token.exchange.pair
  87. const OneInchPrice = token.OneInchPrice ? token.OneInchPrice : NaN
  88. const BinancePrice = token.BinancePrice ? token.BinancePrice : NaN
  89. const DiffPrice = token.DiffPrice
  90. const percentage = NumKit.getSubFloat((DiffPrice / OneInchPrice) * 100, 2)
  91. const orderPrice = token.orderPrice ? token.orderPrice : ''
  92. const orderPercentage = orderPrice ? NumKit.getSubFloat(((BinancePrice - orderPrice) / orderPrice) * 100, 2) : ''
  93. const nextTimeStr = token.nextHandleTime ? TimeKit.getTimeByMillisecond(token.nextHandleTime) : ''
  94. // 价格非法判定
  95. const priceCondition = (() => {
  96. return OneInchPrice && BinancePrice
  97. })()
  98. const rows = [pair.toString(), OneInchPrice.toString(), BinancePrice.toString(),
  99. orderPrice.toString(), orderPercentage.toString(), nextTimeStr]
  100. // 识别到在拉盘
  101. token.isLong = (() => {
  102. const percentageCondition = percentage > PrivateConfig.percentageLimit
  103. const isNoPrevHandleTime = !token.nextHandleTime
  104. const isTimeCover = new Date().getTime() > token.nextHandleTime
  105. const timeCondition = isNoPrevHandleTime || isTimeCover
  106. return priceCondition && percentageCondition && timeCondition
  107. })()
  108. // 打印表格
  109. table.showLine(rows, undefined)
  110. })
  111. table.printEndLine(logger)
  112. logger.info(`${Config.baseIerc20Token.symbol} asset amount: ${accountAssetMap[Config.baseIerc20Token.symbol]}.`)
  113. logger.info('')
  114. }
  115. const priceHandler = async function(context, task) {
  116. const tokenMap = context.tokenMap
  117. const tokenContractAddressList = Object.keys(tokenMap)
  118. for (const tokenHash of tokenContractAddressList) {
  119. const toToken = tokenMap[tokenHash]
  120. const priceTick = toToken.exchange.priceTick
  121. const fromIerc20Token = Config.baseIerc20Token
  122. const amount = PrivateConfig.baseTokenAmount
  123. const OneInchPrice = NumKit.getSubFloat(await OneInch.price(fromIerc20Token.contract, tokenHash, amount), priceTick)
  124. const BinancePrice = NumKit.getSubFloat(await BinanceSpot.realPrice(toToken.exchange.pair), priceTick)
  125. toToken.OneInchPrice = OneInchPrice
  126. toToken.BinancePrice = BinancePrice
  127. toToken.DiffPrice = BinancePrice - OneInchPrice
  128. if ((() => {
  129. return Math.abs(toToken.DiffPrice) < Math.pow(10, -priceTick)
  130. })()) {
  131. toToken.DiffPrice = 0
  132. } {
  133. toToken.DiffPrice = NumKit.getSubFloat(toToken.DiffPrice, priceTick)
  134. }
  135. }
  136. }
  137. const accountHandler = async function(context, task) {
  138. const accountInfoRst = await context.binanceSpot.accountInfo()
  139. context.accountAssetMap = BinanceKit.parseBalancesToAccountAssetMap(accountInfoRst.balances)
  140. }
  141. const onTickFun = async function() {
  142. const task = this
  143. const context = task.context
  144. // 搜集所有价格数据
  145. await priceHandler(context, task)
  146. // 获取账户数据
  147. await accountHandler(context, task)
  148. // 展示关键信息
  149. showInfo(context, task)
  150. // 交易逻辑
  151. await tradeLogic(context, task)
  152. }
  153. const onePro = new OneTask('OnePro', PrivateConfig.delay, OneTask.baseInit, onTickFun)
  154. onePro.Start()