diff --git a/CHANGELOG.md b/CHANGELOG.md index 48bc7524..f94ee0e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +- Fixed timezone inconsistency. Thanks [@uhliksk](https://github.com/uhliksk) - [#454](https://github.com/chrisleekr/binance-trading-bot/pull/454) - Improved UI. Thanks [@uhliksk](https://github.com/uhliksk) - [#452](https://github.com/chrisleekr/binance-trading-bot/pull/452) - Improved error handler and stability. Thanks [@habibalkhabbaz](https://github.com/habibalkhabbaz) - [#448](https://github.com/chrisleekr/binance-trading-bot/pull/448) diff --git a/app/cronjob/trailingTrade/step/__tests__/determine-action.test.js b/app/cronjob/trailingTrade/step/__tests__/determine-action.test.js index c6f55229..0e0a986d 100644 --- a/app/cronjob/trailingTrade/step/__tests__/determine-action.test.js +++ b/app/cronjob/trailingTrade/step/__tests__/determine-action.test.js @@ -1588,7 +1588,7 @@ describe('determine-action.js', () => { }, tradingView: { result: { - time: moment().format(), + time: moment().toISOString(), summary: { RECOMMENDATION: 'SELL' } @@ -1638,7 +1638,7 @@ describe('determine-action.js', () => { }, tradingView: { result: { - time: moment().format(), + time: moment().toISOString(), summary: { RECOMMENDATION: undefined } @@ -1663,7 +1663,7 @@ describe('determine-action.js', () => { }, tradingView: { result: { - time: moment().subtract('6', 'minutes').format(), + time: moment().subtract('6', 'minutes').toISOString(), summary: { RECOMMENDATION: 'SELL' } @@ -1691,7 +1691,7 @@ describe('determine-action.js', () => { }, tradingView: { result: { - time: moment().format(), + time: moment().toISOString(), summary: { RECOMMENDATION: 'SELL' } @@ -1723,7 +1723,7 @@ describe('determine-action.js', () => { }, tradingView: { result: { - time: moment().format(), + time: moment().toISOString(), summary: { RECOMMENDATION: 'SELL' } @@ -1758,7 +1758,7 @@ describe('determine-action.js', () => { }, tradingView: { result: { - time: moment().format(), + time: moment().toISOString(), summary: { RECOMMENDATION: 'SELL' } @@ -1805,7 +1805,7 @@ describe('determine-action.js', () => { }, tradingView: { result: { - time: moment().format(), + time: moment().toISOString(), summary: { RECOMMENDATION: 'SELL' } @@ -1841,7 +1841,7 @@ describe('determine-action.js', () => { }, tradingView: { result: { - time: moment().format(), + time: moment().toISOString(), summary: { RECOMMENDATION: 'NEUTRAL' } @@ -1880,7 +1880,7 @@ describe('determine-action.js', () => { }, tradingView: { result: { - time: moment().format(), + time: moment().toISOString(), summary: { RECOMMENDATION: 'NEUTRAL' } @@ -1916,7 +1916,7 @@ describe('determine-action.js', () => { }, tradingView: { result: { - time: moment().format(), + time: moment().toISOString(), summary: { RECOMMENDATION: 'SELL' } @@ -1952,7 +1952,7 @@ describe('determine-action.js', () => { }, tradingView: { result: { - time: moment().format(), + time: moment().toISOString(), summary: { RECOMMENDATION: 'STRONG_SELL' } @@ -1988,7 +1988,7 @@ describe('determine-action.js', () => { }, tradingView: { result: { - time: moment().format(), + time: moment().toISOString(), summary: { RECOMMENDATION: 'SELL' } @@ -2027,7 +2027,7 @@ describe('determine-action.js', () => { }, tradingView: { result: { - time: moment().format(), + time: moment().toISOString(), summary: { RECOMMENDATION: 'STRONG_SELL' } diff --git a/app/cronjob/trailingTrade/step/determine-action.js b/app/cronjob/trailingTrade/step/determine-action.js index 80a0d33f..a2fcb740 100644 --- a/app/cronjob/trailingTrade/step/determine-action.js +++ b/app/cronjob/trailingTrade/step/determine-action.js @@ -323,8 +323,8 @@ const shouldForceSellByTradingViewRecommendation = (logger, data) => { if (tradingViewUpdatedAt.isBefore(currentTime)) { logger.info( { - tradingViewUpdatedAt: tradingViewUpdatedAt.format(), - currentTime: currentTime.format() + tradingViewUpdatedAt: tradingViewUpdatedAt.toISOString(), + currentTime: currentTime.toISOString() }, `TradingView data is older than ${tradingViewUseOnlyWithin} minutes. Ignore TradingView recommendation.` ); diff --git a/app/cronjob/trailingTrade/step/ensure-grid-trade-order-executed.js b/app/cronjob/trailingTrade/step/ensure-grid-trade-order-executed.js index baaba3d5..33fb73bc 100644 --- a/app/cronjob/trailingTrade/step/ensure-grid-trade-order-executed.js +++ b/app/cronjob/trailingTrade/step/ensure-grid-trade-order-executed.js @@ -274,7 +274,7 @@ const execute = async (logger, rawData) => { ); } else { logger.info( - { lastBuyOrder, currentTime: moment() }, + { lastBuyOrder, currentTime: moment().format() }, 'Skip checking the grid trade last buy order' ); } @@ -328,7 +328,7 @@ const execute = async (logger, rawData) => { ); } else { logger.info( - { lastSellOrder, currentTime: moment() }, + { lastSellOrder, currentTime: moment().format() }, 'Skip checking the grid trade last sell order' ); } diff --git a/app/cronjob/trailingTrade/step/ensure-manual-order.js b/app/cronjob/trailingTrade/step/ensure-manual-order.js index 34456d25..180a1641 100644 --- a/app/cronjob/trailingTrade/step/ensure-manual-order.js +++ b/app/cronjob/trailingTrade/step/ensure-manual-order.js @@ -164,7 +164,10 @@ const execute = async (logger, rawData) => { slackMessageOrderDeleted(logger, symbol, order.side, order); } else { - logger.info({ order, currentTime: moment() }, 'Skip checking the order'); + logger.info( + { order, currentTime: moment().format() }, + 'Skip checking the order' + ); } } diff --git a/app/cronjob/trailingTrade/step/get-balances.js b/app/cronjob/trailingTrade/step/get-balances.js index f9e83cbe..fe8db923 100644 --- a/app/cronjob/trailingTrade/step/get-balances.js +++ b/app/cronjob/trailingTrade/step/get-balances.js @@ -46,7 +46,9 @@ const execute = async (logger, rawData) => { data.baseAssetBalance = baseAssetBalance; data.baseAssetBalance.total = baseAssetTotalBalance; data.baseAssetBalance.estimatedValue = baseAssetEstimatedValue; - data.baseAssetBalance.updatedAt = moment(accountInfo.updateTime).toDate(); + data.baseAssetBalance.updatedAt = moment(accountInfo.updateTime) + .utc() + .toDate(); data.quoteAssetBalance = quoteAssetBalance; diff --git a/app/cronjob/trailingTrade/step/get-override-action.js b/app/cronjob/trailingTrade/step/get-override-action.js index 4bdd1ad5..dfee3965 100644 --- a/app/cronjob/trailingTrade/step/get-override-action.js +++ b/app/cronjob/trailingTrade/step/get-override-action.js @@ -183,7 +183,7 @@ const execute = async (logger, rawData) => { ...overrideData, actionAt: moment() .add(autoTriggerBuyTriggerAfter, 'minutes') - .format() + .toISOString() }, rescheduleReason ); diff --git a/app/cronjob/trailingTrade/step/handle-open-orders.js b/app/cronjob/trailingTrade/step/handle-open-orders.js index f524b1d4..e97563be 100644 --- a/app/cronjob/trailingTrade/step/handle-open-orders.js +++ b/app/cronjob/trailingTrade/step/handle-open-orders.js @@ -119,7 +119,7 @@ const execute = async (logger, rawData) => { symbol, { action: 'buy', - actionAt: moment().format(), + actionAt: moment().toISOString(), triggeredBy: 'buy-cancelled', notify: false, checkTradingView: true @@ -153,7 +153,7 @@ const execute = async (logger, rawData) => { data.accountInfo = await updateAccountInfo( logger, balances, - moment().utc().format() + moment().toISOString() ); } } else { @@ -219,7 +219,7 @@ const execute = async (logger, rawData) => { data.accountInfo = await updateAccountInfo( logger, balances, - moment().utc().format() + moment().toISOString() ); } } else { diff --git a/app/cronjob/trailingTrade/step/place-buy-order.js b/app/cronjob/trailingTrade/step/place-buy-order.js index c44608c5..cdba91b7 100644 --- a/app/cronjob/trailingTrade/step/place-buy-order.js +++ b/app/cronjob/trailingTrade/step/place-buy-order.js @@ -78,8 +78,8 @@ const isAllowedTradingViewRecommendation = (logger, data) => { if (tradingViewIfExpires === 'do-not-buy') { logger.info( { - tradingViewUpdatedAt: tradingViewUpdatedAt.format(), - currentTime: currentTime.format() + tradingViewUpdatedAt: tradingViewUpdatedAt.toISOString(), + currentTime: currentTime.toISOString() }, `TradingView data is older than ${tradingViewUseOnlyWithin} minutes. Do not buy.` ); @@ -93,8 +93,8 @@ const isAllowedTradingViewRecommendation = (logger, data) => { logger.info( { - tradingViewUpdatedAt: tradingViewUpdatedAt.format(), - currentTime: currentTime.format() + tradingViewUpdatedAt: tradingViewUpdatedAt.toISOString(), + currentTime: currentTime.toISOString() }, `TradingView data is older than ${tradingViewUseOnlyWithin} minutes. Ignore TradingView recommendation.` ); @@ -218,7 +218,7 @@ const execute = async (logger, rawData) => { symbol, { action: 'buy', - actionAt: moment().add(1, 'minutes').format(), + actionAt: moment().add(1, 'minutes').toISOString(), triggeredBy: 'buy-order-trading-view', notify: false, checkTradingView: true @@ -462,7 +462,7 @@ const execute = async (logger, rawData) => { data.accountInfo = await updateAccountInfo( logger, balances, - moment().utc().format() + moment().toISOString() ); slack.sendMessage( diff --git a/app/cronjob/trailingTrade/step/remove-last-buy-price.js b/app/cronjob/trailingTrade/step/remove-last-buy-price.js index 6e5f9fc1..e8a4fa7b 100644 --- a/app/cronjob/trailingTrade/step/remove-last-buy-price.js +++ b/app/cronjob/trailingTrade/step/remove-last-buy-price.js @@ -134,7 +134,9 @@ const removeLastBuyPrice = async ( symbol, { action: 'buy', - actionAt: moment().add(autoTriggerBuyTriggerAfter, 'minutes').format(), + actionAt: moment() + .add(autoTriggerBuyTriggerAfter, 'minutes') + .toISOString(), triggeredBy: 'auto-trigger', notify: true, checkTradingView: true diff --git a/app/cronjob/trailingTradeHelper/configuration.js b/app/cronjob/trailingTradeHelper/configuration.js index fa93b5e2..a16459e1 100644 --- a/app/cronjob/trailingTradeHelper/configuration.js +++ b/app/cronjob/trailingTradeHelper/configuration.js @@ -451,12 +451,12 @@ const archiveSymbolGridTrade = async (logger, symbol = null) => { ..._.pick(symbolInfo, ['baseAsset', 'quoteAsset']), ...gridProfit, ..._.omit(symbolGridTrade, 'key', '_id'), - archivedAt: moment().format() + archivedAt: moment().toISOString() }; await saveSymbolGridTradeArchive( logger, - `${symbol}-${moment().format()}`, + `${symbol}-${moment().toISOString()}`, archivedGridTrade ); diff --git a/app/cronjob/trailingTradeIndicator/step/__tests__/get-closed-trades.test.js b/app/cronjob/trailingTradeIndicator/step/__tests__/get-closed-trades.test.js index e2684f4b..69a1972d 100644 --- a/app/cronjob/trailingTradeIndicator/step/__tests__/get-closed-trades.test.js +++ b/app/cronjob/trailingTradeIndicator/step/__tests__/get-closed-trades.test.js @@ -17,19 +17,29 @@ describe('get-closed-trades.js', () => { describe('when mongo.aggregate returns value', () => { [ { - selectedPeriod: undefined + selectedPeriod: undefined, + selectedPeriodTZ: 'UTC', + selectedPeriodLC: 'us' }, { - selectedPeriod: 'd' + selectedPeriod: 'd', + selectedPeriodTZ: 'UTC', + selectedPeriodLC: 'us' }, { - selectedPeriod: 'w' + selectedPeriod: 'w', + selectedPeriodTZ: 'UTC', + selectedPeriodLC: 'us' }, { - selectedPeriod: 'm' + selectedPeriod: 'm', + selectedPeriodTZ: 'UTC', + selectedPeriodLC: 'us' }, { - selectedPeriod: 'a' + selectedPeriod: 'a', + selectedPeriodTZ: 'UTC', + selectedPeriodLC: 'us' } ].forEach(t => { describe(`selectedPeriod: ${t.selectedPeriod}`, () => { @@ -46,7 +56,9 @@ describe('get-closed-trades.js', () => { cacheMock.hget = jest.fn().mockResolvedValue( JSON.stringify({ - selectedPeriod: t.selectedPeriod + selectedPeriod: t.selectedPeriod, + selectedPeriodTZ: t.selectedPeriodTZ, + selectedPeriodLC: t.selectedPeriodLC }) ); @@ -55,18 +67,22 @@ describe('get-closed-trades.js', () => { start = null; end = null; + const momentLocale = moment + .tz(t.selectedPeriodTZ) + .locale(t.selectedPeriodLC); + switch (t.selectedPeriod) { case 'd': - start = moment().startOf('day').toISOString(); - end = moment().endOf('day').toISOString(); + start = momentLocale.startOf('day').toISOString(); + end = momentLocale.endOf('day').toISOString(); break; case 'w': - start = moment().startOf('week').toISOString(); - end = moment().endOf('week').toISOString(); + start = momentLocale.startOf('week').toISOString(); + end = momentLocale.endOf('week').toISOString(); break; case 'm': - start = moment().startOf('month').toISOString(); - end = moment().endOf('month').toISOString(); + start = momentLocale.startOf('month').toISOString(); + end = momentLocale.endOf('month').toISOString(); break; case 'a': default: @@ -74,8 +90,8 @@ describe('get-closed-trades.js', () => { if (start || end) { match.archivedAt = { - ...(start ? { $gte: moment(start).toISOString() } : {}), - ...(end ? { $lte: moment(end).toISOString() } : {}) + ...(start ? { $gte: start } : {}), + ...(end ? { $lte: end } : {}) }; } @@ -176,7 +192,11 @@ describe('get-closed-trades.js', () => { 'closed-trades', JSON.stringify({ selectedPeriod: t.selectedPeriod, - loadedPeriod: t.selectedPeriod || 'a' + selectedPeriodTZ: t.selectedPeriodTZ, + selectedPeriodLC: t.selectedPeriodLC, + loadedPeriod: t.selectedPeriod || 'a', + loadedPeriodTZ: t.selectedPeriodTZ, + loadedPeriodLC: t.selectedPeriodLC }) ); }); @@ -298,7 +318,9 @@ describe('get-closed-trades.js', () => { 'trailing-trade-common', 'closed-trades', JSON.stringify({ - loadedPeriod: 'a' + loadedPeriod: 'a', + loadedPeriodTZ: 'UTC', + loadedPeriodLC: 'us' }) ); }); diff --git a/app/cronjob/trailingTradeIndicator/step/get-closed-trades.js b/app/cronjob/trailingTradeIndicator/step/get-closed-trades.js index b21f5d5a..a78a7e30 100644 --- a/app/cronjob/trailingTradeIndicator/step/get-closed-trades.js +++ b/app/cronjob/trailingTradeIndicator/step/get-closed-trades.js @@ -19,22 +19,29 @@ const execute = async (logger, rawData) => { {}; const selectedPeriod = _.get(closedTradesSetting, 'selectedPeriod', 'a'); + const selectedPeriodTZ = _.get( + closedTradesSetting, + 'selectedPeriodTZ', + 'UTC' + ); + const selectedPeriodLC = _.get(closedTradesSetting, 'selectedPeriodLC', 'us'); let start = null; let end = null; + const momentLocale = moment.tz(selectedPeriodTZ).locale(selectedPeriodLC); switch (selectedPeriod) { case 'd': - start = moment().startOf('day').toISOString(); - end = moment().endOf('day').toISOString(); + start = momentLocale.startOf('day').toISOString(); + end = momentLocale.endOf('day').toISOString(); break; case 'w': - start = moment().startOf('week').toISOString(); - end = moment().endOf('week').toISOString(); + start = momentLocale.startOf('week').toISOString(); + end = momentLocale.endOf('week').toISOString(); break; case 'm': - start = moment().startOf('month').toISOString(); - end = moment().endOf('month').toISOString(); + start = momentLocale.startOf('month').toISOString(); + end = momentLocale.endOf('month').toISOString(); break; case 'a': default: @@ -44,8 +51,8 @@ const execute = async (logger, rawData) => { if (start && end) { match.archivedAt = { - $gte: moment(start).toISOString(), - $lte: moment(end).toISOString() + $gte: start, + $lte: end }; } @@ -121,7 +128,9 @@ const execute = async (logger, rawData) => { 'closed-trades', JSON.stringify({ ...closedTradesSetting, - loadedPeriod: selectedPeriod + loadedPeriod: selectedPeriod, + loadedPeriodTZ: selectedPeriodTZ, + loadedPeriodLC: selectedPeriodLC }) ); diff --git a/app/frontend/webserver/handlers/closed-trades-set-period.js b/app/frontend/webserver/handlers/closed-trades-set-period.js index aef9c02d..5607920d 100644 --- a/app/frontend/webserver/handlers/closed-trades-set-period.js +++ b/app/frontend/webserver/handlers/closed-trades-set-period.js @@ -5,9 +5,12 @@ const handleClosedTradesSetPeriod = async (funcLogger, app) => { endpoint: '/closed-trades-set-period' }); app.route('/closed-trades-set-period').post(async (req, res) => { - const { selectedPeriod } = req.body; + const { selectedPeriod, selectedPeriodTZ, selectedPeriodLC } = req.body; - logger.info({ selectedPeriod }, 'Set period for closed trades'); + logger.info( + { selectedPeriod, selectedPeriodTZ, selectedPeriodLC }, + 'Set period for closed trades' + ); const closedTradesSetting = JSON.parse(await cache.hget('trailing-trade-common', 'closed-trades')) || @@ -18,7 +21,9 @@ const handleClosedTradesSetPeriod = async (funcLogger, app) => { 'closed-trades', JSON.stringify({ ...closedTradesSetting, - selectedPeriod + selectedPeriod, + selectedPeriodTZ, + selectedPeriodLC }) ); diff --git a/app/frontend/websocket/handlers/cancel-order.js b/app/frontend/websocket/handlers/cancel-order.js index 6fb75616..ee3d01e1 100644 --- a/app/frontend/websocket/handlers/cancel-order.js +++ b/app/frontend/websocket/handlers/cancel-order.js @@ -17,7 +17,7 @@ const handleCancelOrder = async (logger, ws, payload) => { { action: 'cancel-order', order, - actionAt: moment().format(), + actionAt: moment().toISOString(), triggeredBy: 'user' }, 'Cancelling the order action has been received. Wait for cancelling the order.' diff --git a/app/frontend/websocket/handlers/dust-transfer-execute.js b/app/frontend/websocket/handlers/dust-transfer-execute.js index 8755bf51..b5b4327b 100644 --- a/app/frontend/websocket/handlers/dust-transfer-execute.js +++ b/app/frontend/websocket/handlers/dust-transfer-execute.js @@ -16,7 +16,7 @@ const handleDustTransferExecute = async (logger, ws, payload) => { { action: 'dust-transfer', params: dustTransfer, - actionAt: moment().format(), + actionAt: moment().toISOString(), triggeredBy: 'user' }, 'The dust transfer request received by the bot. Wait for executing the dust transfer.' diff --git a/app/frontend/websocket/handlers/manual-trade-all-symbols.js b/app/frontend/websocket/handlers/manual-trade-all-symbols.js index 8103738b..bdbcbbb5 100644 --- a/app/frontend/websocket/handlers/manual-trade-all-symbols.js +++ b/app/frontend/websocket/handlers/manual-trade-all-symbols.js @@ -49,7 +49,7 @@ const handleManualTradeAllSymbols = async (logger, ws, payload) => { quoteOrderQty } }, - actionAt: currentTime.format(), + actionAt: currentTime.toISOString(), triggeredBy: 'user' }; @@ -90,7 +90,7 @@ const handleManualTradeAllSymbols = async (logger, ws, payload) => { marketQuantity } }, - actionAt: currentTime.format(), + actionAt: currentTime.toISOString(), triggeredBy: 'user' }; diff --git a/app/frontend/websocket/handlers/manual-trade.js b/app/frontend/websocket/handlers/manual-trade.js index e1217d80..9495c587 100644 --- a/app/frontend/websocket/handlers/manual-trade.js +++ b/app/frontend/websocket/handlers/manual-trade.js @@ -17,7 +17,7 @@ const handleManualTrade = async (logger, ws, payload) => { { action: 'manual-trade', order, - actionAt: moment().format(), + actionAt: moment().toISOString(), triggeredBy: 'user' }, 'The manual order received by the bot. Wait for placing the order.' diff --git a/app/frontend/websocket/handlers/symbol-trigger-buy.js b/app/frontend/websocket/handlers/symbol-trigger-buy.js index cfdc435a..3da7d351 100644 --- a/app/frontend/websocket/handlers/symbol-trigger-buy.js +++ b/app/frontend/websocket/handlers/symbol-trigger-buy.js @@ -16,7 +16,7 @@ const handleSymbolTriggerBuy = async (logger, ws, payload) => { symbol, { action: 'buy', - actionAt: moment().format(), + actionAt: moment().toISOString(), triggeredBy: 'user', notify: true, // For triggering buy action must execute. So don't check TradingView recommendation. diff --git a/app/frontend/websocket/handlers/symbol-trigger-sell.js b/app/frontend/websocket/handlers/symbol-trigger-sell.js index 2a48aecc..aba16fc2 100644 --- a/app/frontend/websocket/handlers/symbol-trigger-sell.js +++ b/app/frontend/websocket/handlers/symbol-trigger-sell.js @@ -16,7 +16,7 @@ const handleSymbolTriggerSell = async (logger, ws, payload) => { symbol, { action: 'sell', - actionAt: moment().format(), + actionAt: moment().toISOString(), triggeredBy: 'user' }, 'The sell order received by the bot. Wait for placing the order.' diff --git a/app/helpers/logger.js b/app/helpers/logger.js index 8f491fde..dc5fe414 100644 --- a/app/helpers/logger.js +++ b/app/helpers/logger.js @@ -24,7 +24,7 @@ InfoStream.prototype.write = rawLog => { mongo.insertOne(fakeLogger, 'trailing-trade-logs', { symbol: log.symbol, msg: log.msg, - loggedAt: moment(log.time).toDate(), + loggedAt: moment(log.time).utc().toDate(), data: _.omit(log, [ 'msg', 'symbol', diff --git a/app/server-binance.js b/app/server-binance.js index 3e06fc5b..0f8983f5 100644 --- a/app/server-binance.js +++ b/app/server-binance.js @@ -1,5 +1,4 @@ const _ = require('lodash'); -// const moment = require('moment-timezone'); const config = require('config'); const { PubSub, cache, mongo } = require('./helpers'); diff --git a/public/index.html b/public/index.html index ecdc84bf..ecc456d3 100644 --- a/public/index.html +++ b/public/index.html @@ -77,7 +77,7 @@ >