|
|
@@ -9,77 +9,90 @@ const TableKit = require('../kit/table-kit')
|
|
|
const BinanceKit = require("../libs/binance/binance-kit");
|
|
|
const assert = require("assert");
|
|
|
|
|
|
-const orderHandler = async function(context, task) {
|
|
|
+const holdingHandler = async function(context, task, token, pair) {
|
|
|
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
|
|
|
+ // 这两个逻辑只有可能满足一个
|
|
|
+ let isStopWin = token.BinancePrice > (token.orderPrice * (100 + PrivateConfig.stopWinPercentage) / 100)
|
|
|
+ let isStopLoss = token.BinancePrice < (token.orderPrice * (100 - PrivateConfig.stopLossPercentage) / 100)
|
|
|
|
|
|
- // 价格非法判定
|
|
|
- if (!token.BinancePrice || !token.OneInchPrice) continue;
|
|
|
+ assert.notEqual(isStopWin && isStopLoss, true, '止盈止损逻辑错误,请认真检查。')
|
|
|
|
|
|
- // 更新token的实际余额
|
|
|
- // token.orderAmount = NumKit.getSubFloat(accountAssetMap[token.exchange.symbol], token.exchange.lotSize)
|
|
|
+ if (isStopWin || isStopLoss) {
|
|
|
+ const orderPrice = token.orderPrice
|
|
|
+ const orderAmount = token.orderAmount
|
|
|
|
|
|
- // 卖出逻辑判定
|
|
|
- if (token.orderPrice && token.orderAmount > Math.pow(10, -token.exchange.lotSize)) {
|
|
|
- // 这两个逻辑只有可能满足一个
|
|
|
- let isStopWin = token.BinancePrice > (token.orderPrice * (100 + PrivateConfig.stopWinPercentage) / 100)
|
|
|
- let isStopLoss = token.BinancePrice < (token.orderPrice * (100 - PrivateConfig.stopLossPercentage) / 100)
|
|
|
+ token.orderPrice = undefined
|
|
|
+ token.nextHandleTime = new Date().getTime() + PrivateConfig.stopLossHandleSpaceHours * (60 * 60 * 1000)
|
|
|
|
|
|
- assert.notEqual(isStopWin && isStopLoss, true, '止盈止损逻辑错误,请认真检查。')
|
|
|
+ try {
|
|
|
+ const sellMarketRst = await binanceSpot.sellMarket(pair, orderAmount)
|
|
|
|
|
|
- if (isStopWin || isStopLoss) {
|
|
|
- const orderPrice = token.orderPrice
|
|
|
- const orderAmount = token.orderAmount
|
|
|
+ const price = NumKit.getSubFloat(sellMarketRst.avgPrice, token.exchange.priceTick)
|
|
|
+ const cummulativeQuoteQty = NumKit.getSubFloat(sellMarketRst.cummulativeQuoteQty, 6)
|
|
|
|
|
|
- token.orderPrice = undefined
|
|
|
- token.nextHandleTime = new Date().getTime() + PrivateConfig.stopLossHandleSpaceHours * (60 * 60 * 1000)
|
|
|
+ fileLogger.info(`[${isStopWin ? '盈+' : '损-'}]${pair}, `
|
|
|
+ .concat(`成交额${cummulativeQuoteQty}, 均价${orderPrice}->${price}, 卖出${orderAmount}个.`)
|
|
|
+ )
|
|
|
+ } catch (e) {
|
|
|
+ fileLogger.error(`[止${isStopWin ? '盈' : '损'}失败]${pair}, ${e}`)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- try {
|
|
|
- const sellMarketRst = await binanceSpot.sellMarket(pair, orderAmount)
|
|
|
+const noHoldingHandler = async function(context, task, token, pair) {
|
|
|
+ const fileLogger = task.fileLogger
|
|
|
+ const binanceSpot = context.binanceSpot
|
|
|
+ const accountAssetMap = context.accountAssetMap
|
|
|
|
|
|
- const price = NumKit.getSubFloat(sellMarketRst.avgPrice, token.exchange.priceTick)
|
|
|
- const cummulativeQuoteQty = NumKit.getSubFloat(sellMarketRst.cummulativeQuoteQty, 6)
|
|
|
+ // 1. 获取base资产数量
|
|
|
+ const baseAssetAmount = accountAssetMap[Config.baseIerc20Token.symbol]
|
|
|
|
|
|
- fileLogger.info(`[${isStopWin ? '盈+' : '损-'}]${pair}, `
|
|
|
- .concat(`成交额${cummulativeQuoteQty}, 均价${orderPrice}->${price}, 卖出${orderAmount}个.`)
|
|
|
- )
|
|
|
- } catch (e) {
|
|
|
- fileLogger.error(`[止${isStopWin ? '盈' : '损'}失败]${pair}, ${e}`)
|
|
|
- }
|
|
|
- }
|
|
|
- } else if (token.isLong) {
|
|
|
- // 1. 获取base资产数量
|
|
|
- const baseAssetAmount = accountAssetMap[Config.baseIerc20Token.symbol]
|
|
|
+ // 2. 判断余额是否够下单
|
|
|
+ if (PrivateConfig.baseTokenAmount > baseAssetAmount) {
|
|
|
+ fileLogger.info(`[余额不足]${pair}, 需要: ${PrivateConfig.baseTokenAmount}, 剩余: ${baseAssetAmount}.`)
|
|
|
+ token.nextHandleTime = new Date().getTime() + PrivateConfig.stopLossHandleSpaceHours * (60 * 60 * 1000)
|
|
|
|
|
|
- // 2. 判断余额是否够下单
|
|
|
- if (PrivateConfig.baseTokenAmount > baseAssetAmount) {
|
|
|
- fileLogger.info(`[余额不足]${pair}, 需要: ${PrivateConfig.baseTokenAmount}, 剩余: ${baseAssetAmount}.`)
|
|
|
- token.nextHandleTime = new Date().getTime() + PrivateConfig.stopLossHandleSpaceHours * (60 * 60 * 1000)
|
|
|
+ return
|
|
|
+ }
|
|
|
|
|
|
- continue
|
|
|
- }
|
|
|
+ // 3. 下单获取成交信息
|
|
|
+ try {
|
|
|
+ const buyMarketRst = await binanceSpot.buyMarket(pair, PrivateConfig.baseTokenAmount)
|
|
|
|
|
|
- // 3. 下单获取成交信息
|
|
|
- try {
|
|
|
- const buyMarketRst = await binanceSpot.buyMarket(pair, PrivateConfig.baseTokenAmount)
|
|
|
+ token.cummulativeQuoteQty = NumKit.getSubFloat(buyMarketRst.cummulativeQuoteQty, 6)
|
|
|
+ token.orderAmount = NumKit.getSubFloat(buyMarketRst.executedQty, token.exchange.lotSize)
|
|
|
+ token.orderPrice = NumKit.getSubFloat(buyMarketRst.avgPrice, token.exchange.priceTick)
|
|
|
|
|
|
- token.cummulativeQuoteQty = NumKit.getSubFloat(buyMarketRst.cummulativeQuoteQty, 6)
|
|
|
- token.orderAmount = NumKit.getSubFloat(buyMarketRst.executedQty, token.exchange.lotSize)
|
|
|
- token.orderPrice = NumKit.getSubFloat(buyMarketRst.avgPrice, token.exchange.priceTick)
|
|
|
+ fileLogger.info(`[单]${pair}, 成交额${token.cummulativeQuoteQty}, 买入${token.orderAmount}个, 均价${token.orderPrice}.`)
|
|
|
|
|
|
- fileLogger.info(`[单]${pair}, 成交额${token.cummulativeQuoteQty}, 买入${token.orderAmount}个, 均价${token.orderPrice}.`)
|
|
|
+ accountAssetMap[Config.baseIerc20Token.symbol] -= token.cummulativeQuoteQty
|
|
|
+ } catch (e) {
|
|
|
+ fileLogger.error(`[下单失败]${pair}, ${e}`)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const tradeLogic = async function(context, task) {
|
|
|
+ const tokenMap = context.tokenMap
|
|
|
+
|
|
|
+ // 逐对处理交易信息
|
|
|
+ for (const tokenHash of Object.keys(tokenMap)) {
|
|
|
+ const token = tokenMap[tokenHash]
|
|
|
+ const pair = token.exchange.pair
|
|
|
|
|
|
- accountAssetMap[Config.baseIerc20Token.symbol] -= token.cummulativeQuoteQty
|
|
|
- } catch (e) {
|
|
|
- fileLogger.error(`[下单失败]${pair}, ${e}`)
|
|
|
- }
|
|
|
+ // 价格非法判定
|
|
|
+ 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)) {
|
|
|
+ await holdingHandler(context, task, token, pair)
|
|
|
+ } else if (token.isLong) {
|
|
|
+ await noHoldingHandler(context, task, token, pair)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -174,8 +187,8 @@ const onTickFun = async function() {
|
|
|
await accountHandler(context, task)
|
|
|
// 展示关键信息
|
|
|
showInfo(context, task)
|
|
|
- // 交易、订单处理
|
|
|
- await orderHandler(context, task)
|
|
|
+ // 交易逻辑
|
|
|
+ await tradeLogic(context, task)
|
|
|
}
|
|
|
|
|
|
const onePro = new OneTask('OnePro', PrivateConfig.delay, OneTask.baseInit, onTickFun)
|