one-pro.js 7.5 KB

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