|
|
@@ -5,52 +5,101 @@ 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 = function(context, task) {
|
|
|
+const orderHandler = async function(context, task) {
|
|
|
const fileLogger = task.fileLogger
|
|
|
const tokenMap = context.tokenMap
|
|
|
+ const binanceSpot = context.binanceSpot
|
|
|
+ const accountAssetMap = context.accountAssetMap
|
|
|
|
|
|
// 逐对处理交易信息
|
|
|
- Object.keys(tokenMap).forEach((tokenHash) => {
|
|
|
+ for (const tokenHash of Object.keys(tokenMap)) {
|
|
|
const token = tokenMap[tokenHash]
|
|
|
const pair = token.exchange.pair
|
|
|
|
|
|
// 价格非法判定
|
|
|
- if (!token.BinancePrice || !token.OneInchPrice) return
|
|
|
+ if (!token.BinancePrice || !token.OneInchPrice) continue;
|
|
|
|
|
|
// 卖出逻辑判定
|
|
|
if (token.orderPrice) {
|
|
|
- // TODO 对接卖出交易
|
|
|
if (token.BinancePrice > token.orderPrice) {
|
|
|
// 止盈逻辑
|
|
|
const isStopWin = token.BinancePrice > (token.orderPrice * (100 + Config.stopWinPercentage) / 100)
|
|
|
if (isStopWin) {
|
|
|
- fileLogger.info(`[止盈]${pair}, price: ${token.orderPrice}->${token.BinancePrice}, amount: ${token.orderAmount}.`)
|
|
|
- token.orderPrice = undefined
|
|
|
- token.nextHandleTime = new Date().getTime() + Config.stopWinHandleSpaceHours * (60 * 60 * 1000)
|
|
|
+ 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)
|
|
|
+
|
|
|
+ fileLogger.info(`[订单]${pair}, volume:${cummulativeQuoteQty}, 买入${executedQty}, 均价${token.orderPrice}.`)
|
|
|
+ } 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) {
|
|
|
- fileLogger.info(`[止损]${pair}, price: ${token.orderPrice}->${token.BinancePrice}, amount: ${token.orderAmount}.`)
|
|
|
- token.orderPrice = undefined
|
|
|
- token.nextHandleTime = new Date().getTime() + Config.stopLossHandleSpaceHours * (60 * 60 * 1000)
|
|
|
+ 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)
|
|
|
+
|
|
|
+ fileLogger.info(`[订单]${pair}, volume:${cummulativeQuoteQty}, 买入${executedQty}, 均价${token.orderPrice}.`)
|
|
|
+ } else {
|
|
|
+ token.orderPrice = undefined
|
|
|
+ token.nextHandleTime = new Date().getTime() + Config.stopLossHandleSpaceHours * (60 * 60 * 1000)
|
|
|
+
|
|
|
+ fileLogger.error(`[止损失败]${pair}, ${JSON.stringify(sellRst)}`)
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
} else if (token.isLong) {
|
|
|
- // TODO 对接买入交易
|
|
|
- token.orderBaseAmount = Config.baseTokenAmount
|
|
|
-
|
|
|
// 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. 下单获取成交数量以及平均价格
|
|
|
- token.orderPrice = token.BinancePrice
|
|
|
- token.orderAmount = NumKit.getSubFloat(Config.baseTokenAmount / token.orderPrice, token.exchange.lotSize)
|
|
|
+ const buyRst = await binanceSpot.buy(pair, -1, Config.baseTokenAmount)
|
|
|
+
|
|
|
+ if (buyRst.executedQty) {
|
|
|
+ const cummulativeQuoteQty = NumKit.getSubFloat(buyRst.cummulativeQuoteQty, 6)
|
|
|
+ const executedQty = NumKit.getSubFloat(buyRst.executedQty, token.exchange.lotSize)
|
|
|
|
|
|
- // 3. 下单成功后打印日志
|
|
|
- fileLogger.info(`[订单]${pair}, volume: ${Config.baseTokenAmount}, price: ${token.orderPrice}, amount: ${token.orderAmount}.`)
|
|
|
+ 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)
|
|
|
@@ -123,16 +172,23 @@ const priceHandler = async function(context, task) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+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)
|
|
|
// 交易、订单处理
|
|
|
- orderHandler(context, task)
|
|
|
+ await orderHandler(context, task)
|
|
|
}
|
|
|
|
|
|
const onePro = new OneTask('OnePro', Config.delay, OneTask.baseInit, onTickFun)
|