diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cc31c01..7f172092 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,9 +8,10 @@ All notable changes to this project will be documented in this file. - Fixed the issue that cannot export huge logs - [#561](https://github.com/chrisleekr/binance-trading-bot/pull/561), [#567](https://github.com/chrisleekr/binance-trading-bot/pull/567) - Fixed the balance calculation to include dust balances by [@uhliksk](https://github.com/uhliksk) - [#571](https://github.com/chrisleekr/binance-trading-bot/pull/571) - Fixed the open orders to be cancelled when the current price is higher/lower than the order price by [@uhliksk](https://github.com/uhliksk) - [#569](https://github.com/chrisleekr/binance-trading-bot/pull/569) -- Improved queue processing by replacing Bull queue to customised queue system by [@uhliksk](https://github.com/uhliksk) - [#562](https://github.com/chrisleekr/binance-trading-bot/pull/562), [#581](https://github.com/chrisleekr/binance-trading-bot/pull/581) +- Improved queue processing by replacing Bull queue to customised queue system by [@uhliksk](https://github.com/uhliksk) - [#562](https://github.com/chrisleekr/binance-trading-bot/pull/562), [#581](https://github.com/chrisleekr/binance-trading-bot/pull/581) +- Added conservative sell strategy, which can reduce the sell trigger price as the grid gets deeper by [@rando128](https://github.com/rando128) - [#585](https://github.com/chrisleekr/binance-trading-bot/pull/585) -Thanks [@uhliksk](https://github.com/uhliksk) for your great contributions. 💯 :heart: +Thanks [@uhliksk](https://github.com/uhliksk) and [@rando128](https://github.com/rando128) for your great contributions. 💯 :heart: ## [0.0.96] - 2022-12-28 diff --git a/app/cronjob/trailingTrade/step/__tests__/get-indicators.test.js b/app/cronjob/trailingTrade/step/__tests__/get-indicators.test.js index 04e470a1..ca5d70fe 100644 --- a/app/cronjob/trailingTrade/step/__tests__/get-indicators.test.js +++ b/app/cronjob/trailingTrade/step/__tests__/get-indicators.test.js @@ -133,7 +133,11 @@ describe('get-indicators.js', () => { triggerPercentage: 1.06, limitPercentage: 0.979 }, - stopLoss: { maxLossPercentage: 0.8 } + stopLoss: { maxLossPercentage: 0.8 }, + conservativeMode: { + enabled: false, + factor: 0.5 + } } }, baseAssetBalance: { total: 0.1 }, @@ -177,6 +181,10 @@ describe('get-indicators.js', () => { }, stopLoss: { maxLossPercentage: 0.8 + }, + conservativeMode: { + enabled: false, + factor: 0.5 } } }, @@ -219,6 +227,8 @@ describe('get-indicators.js', () => { openOrders: [], stopLossDifference: null, stopLossTriggerPrice: null, + conservativeModeApplicable: false, + triggerPercentage: null, processMessage: '', updatedAt: expect.any(Object) }, @@ -354,7 +364,11 @@ describe('get-indicators.js', () => { triggerPercentage: 1.06, limitPercentage: 0.979 }, - stopLoss: { maxLossPercentage: 0.8 } + stopLoss: { maxLossPercentage: 0.8 }, + conservativeMode: { + enabled: false, + factor: 0.5 + } } }, baseAssetBalance: { total: 0.1 }, @@ -398,6 +412,10 @@ describe('get-indicators.js', () => { }, stopLoss: { maxLossPercentage: 0.8 + }, + conservativeMode: { + enabled: false, + factor: 0.5 } } }, @@ -440,6 +458,8 @@ describe('get-indicators.js', () => { openOrders: [], stopLossDifference: null, stopLossTriggerPrice: null, + conservativeModeApplicable: false, + triggerPercentage: null, processMessage: '', updatedAt: expect.any(Object) }, @@ -573,7 +593,11 @@ describe('get-indicators.js', () => { }, sell: { currentGridTrade: null, - stopLoss: { maxLossPercentage: 0.8 } + stopLoss: { maxLossPercentage: 0.8 }, + conservativeMode: { + enabled: false, + factor: 0.5 + } } }, baseAssetBalance: { total: 0.1 }, @@ -614,6 +638,10 @@ describe('get-indicators.js', () => { currentGridTrade: null, stopLoss: { maxLossPercentage: 0.8 + }, + conservativeMode: { + enabled: false, + factor: 0.5 } } }, @@ -655,6 +683,8 @@ describe('get-indicators.js', () => { currentProfitPercentage: 72.83433333333333, stopLossDifference: 53.712900407519335, stopLossTriggerPrice: 7200, + conservativeModeApplicable: false, + triggerPercentage: null, openOrders: [], processMessage: '', updatedAt: expect.any(Object) @@ -710,7 +740,11 @@ describe('get-indicators.js', () => { triggerPercentage: 1.06, limitPercentage: 0.979 }, - stopLoss: { maxLossPercentage: 0.8 } + stopLoss: { maxLossPercentage: 0.8 }, + conservativeMode: { + enabled: false, + factor: 0.5 + } } }, baseAssetBalance: { total: 0.1 }, @@ -757,6 +791,10 @@ describe('get-indicators.js', () => { }, stopLoss: { maxLossPercentage: 0.8 + }, + conservativeMode: { + enabled: false, + factor: 0.5 } } }, @@ -799,6 +837,8 @@ describe('get-indicators.js', () => { stopLossDifference: 53.712900407519335, stopLossTriggerPrice: 7200, openOrders: [], + conservativeModeApplicable: false, + triggerPercentage: 1.06, processMessage: '', updatedAt: expect.any(Object) }, @@ -853,7 +893,11 @@ describe('get-indicators.js', () => { triggerPercentage: 1.06, limitPercentage: 0.979 }, - stopLoss: { maxLossPercentage: 0.8 } + stopLoss: { maxLossPercentage: 0.8 }, + conservativeMode: { + enabled: false, + factor: 0.5 + } } }, baseAssetBalance: { total: 0.1 }, @@ -900,6 +944,10 @@ describe('get-indicators.js', () => { }, stopLoss: { maxLossPercentage: 0.8 + }, + conservativeMode: { + enabled: false, + factor: 0.5 } } }, @@ -942,6 +990,177 @@ describe('get-indicators.js', () => { stopLossDifference: 53.712900407519335, stopLossTriggerPrice: 7200, openOrders: [], + conservativeModeApplicable: false, + triggerPercentage: 1.06, + processMessage: '', + updatedAt: expect.any(Object) + }, + tradingView: { + request: { + symbol: 'BTCUSDT', + screener: 'CRYPTO', + exchange: 'BINANCE', + interval: '15m' + }, + result: { + summary: { + RECOMMENDATION: 'SELL', + BUY: 4, + SELL: 14, + NEUTRAL: 8 + } + } + } + }); + }); + }); + + describe('when buy grid trade index is 1 and conservative mode enabled', () => { + beforeEach(async () => { + step = require('../get-indicators'); + + rawData = { + symbol: 'BTCUSDT', + symbolInfo: { + filterMinNotional: { minNotional: '10.000' } + }, + symbolConfiguration: { + candles: { limit: '100' }, + buy: { + currentGridTradeIndex: 1, + currentGridTrade: { + triggerPercentage: 1.01, + limitPercentage: 1.021 + }, + athRestriction: { + enabled: true, + restrictionPercentage: 0.9, + candles: { + interval: '1d', + limit: 30 + } + }, + gridTrade: [ + { + executed: true + }, + { + executed: true + } + ] + }, + sell: { + currentGridTrade: { + triggerPercentage: 1.06, + limitPercentage: 0.979 + }, + stopLoss: { maxLossPercentage: 0.8 }, + conservativeMode: { + enabled: true, + factor: 0.5 + } + } + }, + baseAssetBalance: { total: 0.1 }, + openOrders: [] + }; + + result = await step.execute(loggerMock, rawData); + }); + + it('triggers getLastBuyPrice', () => { + expect(mockGetLastBuyPrice).toHaveBeenCalledWith( + loggerMock, + 'BTCUSDT' + ); + }); + + it('triggers expected value', () => { + expect(result).toStrictEqual({ + symbol: 'BTCUSDT', + symbolInfo: { + filterMinNotional: { minNotional: '10.000' } + }, + symbolConfiguration: { + candles: { limit: '100' }, + buy: { + currentGridTradeIndex: 1, + currentGridTrade: { + triggerPercentage: 1.01, + limitPercentage: 1.021 + }, + athRestriction: { + enabled: true, + restrictionPercentage: 0.9, + candles: { + interval: '1d', + limit: 30 + } + }, + gridTrade: [ + { + executed: true + }, + { + executed: true + } + ] + }, + sell: { + currentGridTrade: { + triggerPercentage: 1.06, + limitPercentage: 0.979 + }, + stopLoss: { + maxLossPercentage: 0.8 + }, + conservativeMode: { + enabled: true, + factor: 0.5 + } + } + }, + baseAssetBalance: { + total: 0.1, + estimatedValue: 1555.509, + isLessThanMinNotionalValue: false + }, + openOrders: [], + indicators: { + highestPrice: 10000, + lowestPrice: 8893.03, + athPrice: 9000 + }, + lastCandle: { + symbol: 'BTCUSDT', + close: '15555.09000000' + }, + buy: { + currentPrice: 15555.09, + limitPrice: 15881.746889999999, + highestPrice: 10000, + lowestPrice: 8893.03, + athPrice: 9000, + athRestrictionPrice: 8100, + triggerPrice: 9090, + difference: 71.12310231023102, + openOrders: [], + processMessage: '', + updatedAt: expect.any(Object) + }, + sell: { + currentPrice: 15555.09, + limitPrice: 15228.43311, + lastBuyPrice: 9000, + triggerPrice: 9270, + difference: 40.40535927468115, + currentProfit: 655.509, + currentProfitPercentage: 72.83433333333333, + stopLossDifference: 53.712900407519335, + stopLossTriggerPrice: 7200, + openOrders: [], + conservativeModeApplicable: true, + triggerPercentage: 1.03, processMessage: '', updatedAt: expect.any(Object) }, @@ -1077,7 +1296,11 @@ describe('get-indicators.js', () => { }, sell: { currentGridTrade: null, - stopLoss: { maxLossPercentage: 0.8 } + stopLoss: { maxLossPercentage: 0.8 }, + conservativeMode: { + enabled: false, + factor: 0.5 + } } }, baseAssetBalance: { total: 0.1 }, @@ -1148,6 +1371,10 @@ describe('get-indicators.js', () => { currentGridTrade: null, stopLoss: { maxLossPercentage: 0.8 + }, + conservativeMode: { + enabled: false, + factor: 0.5 } } }, @@ -1275,6 +1502,8 @@ describe('get-indicators.js', () => { updatedAt: expect.any(Object) } ], + conservativeModeApplicable: false, + triggerPercentage: null, processMessage: '', updatedAt: expect.any(Object) }, @@ -1329,7 +1558,11 @@ describe('get-indicators.js', () => { triggerPercentage: 1.06, limitPercentage: 0.979 }, - stopLoss: { maxLossPercentage: 0.8 } + stopLoss: { maxLossPercentage: 0.8 }, + conservativeMode: { + enabled: false, + factor: 0.5 + } } }, baseAssetBalance: { total: 0.1 }, @@ -1406,6 +1639,10 @@ describe('get-indicators.js', () => { }, stopLoss: { maxLossPercentage: 0.8 + }, + conservativeMode: { + enabled: false, + factor: 0.5 } } }, @@ -1533,6 +1770,8 @@ describe('get-indicators.js', () => { updatedAt: expect.any(Object) } ], + conservativeModeApplicable: false, + triggerPercentage: 1.06, processMessage: '', updatedAt: expect.any(Object) }, @@ -1587,7 +1826,11 @@ describe('get-indicators.js', () => { triggerPercentage: 1.06, limitPercentage: 0.979 }, - stopLoss: { maxLossPercentage: 0.8 } + stopLoss: { maxLossPercentage: 0.8 }, + conservativeMode: { + enabled: false, + factor: 0.5 + } } }, baseAssetBalance: { total: 0.1 }, @@ -1664,6 +1907,10 @@ describe('get-indicators.js', () => { }, stopLoss: { maxLossPercentage: 0.8 + }, + conservativeMode: { + enabled: false, + factor: 0.5 } } }, @@ -1791,6 +2038,292 @@ describe('get-indicators.js', () => { updatedAt: expect.any(Object) } ], + conservativeModeApplicable: false, + triggerPercentage: 1.06, + processMessage: '', + updatedAt: expect.any(Object) + }, + tradingView: { + request: { + symbol: 'BTCUSDT', + screener: 'CRYPTO', + exchange: 'BINANCE', + interval: '15m' + }, + result: { + summary: { + RECOMMENDATION: 'SELL', + BUY: 4, + SELL: 14, + NEUTRAL: 8 + } + } + } + }); + }); + }); + + describe('when buy grid trade index is 1 with conservative mode enabled', () => { + beforeEach(async () => { + step = require('../get-indicators'); + + rawData = { + symbol: 'BTCUSDT', + symbolInfo: { + filterMinNotional: { minNotional: '10.000' } + }, + symbolConfiguration: { + candles: { limit: '100' }, + buy: { + currentGridTradeIndex: 1, + currentGridTrade: { + triggerPercentage: 1.01, + limitPercentage: 1.021 + }, + athRestriction: { + enabled: true, + restrictionPercentage: 0.9, + candles: { + interval: '1d', + limit: 30 + } + }, + gridTrade: [ + { + executed: true + }, + { + executed: true + } + ] + }, + sell: { + currentGridTrade: { + triggerPercentage: 1.06, + limitPercentage: 0.979 + }, + stopLoss: { maxLossPercentage: 0.8 }, + conservativeMode: { + enabled: true, + factor: 0.5 + } + } + }, + baseAssetBalance: { total: 0.1 }, + openOrders: [ + { + orderId: 1, + symbol: 'BTCUSDT', + type: 'LIMIT', + side: 'BUY', + price: '13000.000', + origQty: '0.005', + time: 1615465601162 + }, + { + orderId: 2, + symbol: 'BTCUSDT', + type: 'STOP_LOSS_LIMIT', + side: 'BUY', + price: '16000.000', + origQty: '0.005', + stopPrice: '16100.000', + time: 1615465601162 + }, + { + orderId: 3, + symbol: 'BTCUSDT', + type: 'STOP_LOSS_LIMIT', + side: 'SELL', + price: '16000.000', + origQty: '0.005', + stopPrice: '15900.000', + time: 1615465601162 + } + ] + }; + + result = await step.execute(loggerMock, rawData); + }); + + it('triggers getLastBuyPrice', () => { + expect(mockGetLastBuyPrice).toHaveBeenCalledWith( + loggerMock, + 'BTCUSDT' + ); + }); + + it('triggers expected value', () => { + expect(result).toStrictEqual({ + symbol: 'BTCUSDT', + symbolInfo: { + filterMinNotional: { minNotional: '10.000' } + }, + symbolConfiguration: { + candles: { limit: '100' }, + buy: { + currentGridTradeIndex: 1, + currentGridTrade: { + triggerPercentage: 1.01, + limitPercentage: 1.021 + }, + athRestriction: { + enabled: true, + restrictionPercentage: 0.9, + candles: { + interval: '1d', + limit: 30 + } + }, + gridTrade: [ + { + executed: true + }, + { + executed: true + } + ] + }, + sell: { + currentGridTrade: { + triggerPercentage: 1.06, + limitPercentage: 0.979 + }, + stopLoss: { + maxLossPercentage: 0.8 + }, + conservativeMode: { + enabled: true, + factor: 0.5 + } + } + }, + baseAssetBalance: { + total: 0.1, + estimatedValue: 1555.509, + isLessThanMinNotionalValue: false + }, + openOrders: [ + { + orderId: 1, + symbol: 'BTCUSDT', + type: 'LIMIT', + side: 'BUY', + price: '13000.000', + origQty: '0.005', + time: 1615465601162, + currentPrice: 15555.09, + updatedAt: expect.any(Object) + }, + { + orderId: 2, + symbol: 'BTCUSDT', + type: 'STOP_LOSS_LIMIT', + side: 'BUY', + price: '16000.000', + origQty: '0.005', + stopPrice: '16100.000', + time: 1615465601162, + currentPrice: 15555.09, + differenceToCancel: -1.37423868741684, + differenceToExecute: -3.5030976998525976, + updatedAt: expect.any(Object) + }, + { + orderId: 3, + symbol: 'BTCUSDT', + type: 'STOP_LOSS_LIMIT', + side: 'SELL', + price: '16000.000', + origQty: '0.005', + stopPrice: '15900.000', + time: 1615465601162, + currentPrice: 15555.09, + differenceToCancel: -4.40995396669539, + differenceToExecute: -2.2173449333947826, + minimumProfit: 35, + minimumProfitPercentage: 77.77777777777777, + updatedAt: expect.any(Object) + } + ], + indicators: { + highestPrice: 10000, + lowestPrice: 8893.03, + athPrice: 9000 + }, + lastCandle: { + symbol: 'BTCUSDT', + close: '15555.09000000' + }, + buy: { + currentPrice: 15555.09, + limitPrice: 15881.746889999999, + highestPrice: 10000, + lowestPrice: 8893.03, + athPrice: 9000, + athRestrictionPrice: 8100, + triggerPrice: 9090, + difference: 71.12310231023102, + openOrders: [ + { + orderId: 1, + symbol: 'BTCUSDT', + type: 'LIMIT', + side: 'BUY', + price: '13000.000', + origQty: '0.005', + time: 1615465601162, + currentPrice: 15555.09, + updatedAt: expect.any(Object) + }, + { + orderId: 2, + symbol: 'BTCUSDT', + type: 'STOP_LOSS_LIMIT', + side: 'BUY', + price: '16000.000', + origQty: '0.005', + stopPrice: '16100.000', + time: 1615465601162, + currentPrice: 15555.09, + differenceToCancel: -1.37423868741684, + differenceToExecute: -3.5030976998525976, + updatedAt: expect.any(Object) + } + ], + processMessage: '', + updatedAt: expect.any(Object) + }, + sell: { + currentPrice: 15555.09, + limitPrice: 15228.43311, + lastBuyPrice: 9000, + triggerPrice: 9270, + difference: 40.40535927468115, + currentProfit: 655.509, + currentProfitPercentage: 72.83433333333333, + stopLossDifference: 53.712900407519335, + stopLossTriggerPrice: 7200, + openOrders: [ + { + orderId: 3, + symbol: 'BTCUSDT', + type: 'STOP_LOSS_LIMIT', + side: 'SELL', + price: '16000.000', + origQty: '0.005', + stopPrice: '15900.000', + time: 1615465601162, + currentPrice: 15555.09, + differenceToCancel: -4.40995396669539, + differenceToExecute: -2.2173449333947826, + minimumProfit: 35, + minimumProfitPercentage: 77.77777777777777, + updatedAt: expect.any(Object) + } + ], + conservativeModeApplicable: true, + triggerPercentage: 1.03, processMessage: '', updatedAt: expect.any(Object) }, @@ -1926,7 +2459,11 @@ describe('get-indicators.js', () => { triggerPercentage: 1.06, limitPercentage: 0.979 }, - stopLoss: { maxLossPercentage: 0.8 } + stopLoss: { maxLossPercentage: 0.8 }, + conservativeMode: { + enabled: false, + factor: 0.5 + } } }, baseAssetBalance: { @@ -1998,6 +2535,10 @@ describe('get-indicators.js', () => { }, stopLoss: { maxLossPercentage: 0.8 + }, + conservativeMode: { + enabled: false, + factor: 0.5 } } }, @@ -2125,6 +2666,8 @@ describe('get-indicators.js', () => { updatedAt: expect.any(Object) } ], + conservativeModeApplicable: false, + triggerPercentage: null, processMessage: '', updatedAt: expect.any(Object) }, @@ -2260,7 +2803,11 @@ describe('get-indicators.js', () => { triggerPercentage: 0.99, limitPercentage: 0.98 }, - stopLoss: { maxLossPercentage: 0.8 } + stopLoss: { maxLossPercentage: 0.8 }, + conservativeMode: { + enabled: false, + factor: 0.5 + } } }, baseAssetBalance: { @@ -2308,6 +2855,10 @@ describe('get-indicators.js', () => { }, stopLoss: { maxLossPercentage: 0.8 + }, + conservativeMode: { + enabled: false, + factor: 0.5 } } }, @@ -2350,6 +2901,8 @@ describe('get-indicators.js', () => { stopLossDifference: null, stopLossTriggerPrice: null, openOrders: [], + conservativeModeApplicable: false, + triggerPercentage: null, processMessage: 'World', updatedAt: expect.any(Object) }, @@ -2425,7 +2978,11 @@ describe('get-indicators.js', () => { triggerPercentage: 1.06, limitPercentage: 0.979 }, - stopLoss: { maxLossPercentage: 0.8 } + stopLoss: { maxLossPercentage: 0.8 }, + conservativeMode: { + enabled: false, + factor: 0.5 + } } }, baseAssetBalance: { total: 0.1 }, @@ -2463,7 +3020,11 @@ describe('get-indicators.js', () => { triggerPercentage: 1.06, limitPercentage: 0.979 }, - stopLoss: { maxLossPercentage: 0.8 } + stopLoss: { maxLossPercentage: 0.8 }, + conservativeMode: { + enabled: false, + factor: 0.5 + } } }, baseAssetBalance: { total: 0.1 }, @@ -2558,7 +3119,11 @@ describe('get-indicators.js', () => { triggerPercentage: 1.06, limitPercentage: 0.979 }, - stopLoss: { maxLossPercentage: 0.8 } + stopLoss: { maxLossPercentage: 0.8 }, + conservativeMode: { + enabled: false, + factor: 0.5 + } } }, baseAssetBalance: { total: 0.1 }, @@ -2598,6 +3163,10 @@ describe('get-indicators.js', () => { }, stopLoss: { maxLossPercentage: 0.8 + }, + conservativeMode: { + enabled: false, + factor: 0.5 } } }, @@ -2642,6 +3211,8 @@ describe('get-indicators.js', () => { stopLossDifference: null, stopLossTriggerPrice: null, openOrders: [], + conservativeModeApplicable: false, + triggerPercentage: null, processMessage: '', updatedAt: expect.any(Object) }, @@ -2742,7 +3313,11 @@ describe('get-indicators.js', () => { triggerPercentage: 1.06, limitPercentage: 0.979 }, - stopLoss: { maxLossPercentage: 0.8 } + stopLoss: { maxLossPercentage: 0.8 }, + conservativeMode: { + enabled: false, + factor: 0.5 + } } }, baseAssetBalance: { @@ -2784,6 +3359,10 @@ describe('get-indicators.js', () => { }, stopLoss: { maxLossPercentage: 0.8 + }, + conservativeMode: { + enabled: false, + factor: 0.5 } } }, @@ -2826,6 +3405,8 @@ describe('get-indicators.js', () => { stopLossDifference: null, stopLossTriggerPrice: null, openOrders: [], + conservativeModeApplicable: false, + triggerPercentage: null, processMessage: '', updatedAt: expect.any(Object) }, diff --git a/app/cronjob/trailingTrade/step/get-indicators.js b/app/cronjob/trailingTrade/step/get-indicators.js index 54c7d013..b90604e1 100644 --- a/app/cronjob/trailingTrade/step/get-indicators.js +++ b/app/cronjob/trailingTrade/step/get-indicators.js @@ -49,6 +49,7 @@ const execute = async (logger, rawData) => { buy: { currentGridTradeIndex: currentBuyGridTradeIndex, currentGridTrade: currentBuyGridTrade, + gridTrade: buyGridTrade, athRestriction: { enabled: buyATHRestrictionEnabled, candles: { @@ -60,7 +61,11 @@ const execute = async (logger, rawData) => { }, sell: { currentGridTrade: currentSellGridTrade, - stopLoss: { maxLossPercentage: sellMaxLossPercentage } + stopLoss: { maxLossPercentage: sellMaxLossPercentage }, + conservativeMode: { + enabled: conservativeModeEnabled, + factor: conservativeFactor + } } }, baseAssetBalance: { total: baseAssetTotalBalance }, @@ -224,6 +229,8 @@ const execute = async (logger, rawData) => { let sellTriggerPrice = null; let sellDifference = null; let sellLimitPrice = null; + let conservativeModeApplicable = false; + let triggerPercentage = null; if (lastBuyPrice > 0 && currentSellGridTrade !== null) { const { @@ -231,7 +238,21 @@ const execute = async (logger, rawData) => { limitPercentage: sellLimitPercentage } = currentSellGridTrade; - sellTriggerPrice = lastBuyPrice * sellTriggerPercentage; + const lastExecutedBuyTradeIndex = _.findLastIndex( + buyGridTrade, + trade => trade.executed === true + ); + + conservativeModeApplicable = + conservativeModeEnabled && lastExecutedBuyTradeIndex >= 1; + + triggerPercentage = conservativeModeApplicable + ? 1 + + (sellTriggerPercentage - 1) * + conservativeFactor ** lastExecutedBuyTradeIndex + : sellTriggerPercentage; + + sellTriggerPrice = lastBuyPrice * triggerPercentage; sellDifference = (1 - sellTriggerPrice / currentPrice) * 100; sellLimitPrice = currentPrice * sellLimitPercentage; } @@ -326,6 +347,8 @@ const execute = async (logger, rawData) => { stopLossDifference: sellStopLossDifference, currentProfit: sellCurrentProfit, currentProfitPercentage: sellCurrentProfitPercentage, + conservativeModeApplicable, + triggerPercentage, openOrders: newOpenOrders?.filter(o => o.side.toLowerCase() === 'sell'), processMessage: _.get(data, 'sell.processMessage', ''), updatedAt: moment().utc().toDate() diff --git a/config/custom-environment-variables.json b/config/custom-environment-variables.json index b890d921..26f8e755 100644 --- a/config/custom-environment-variables.json +++ b/config/custom-environment-variables.json @@ -311,6 +311,18 @@ "__format": "boolean" } } + }, + "conservativeMode": { + "enabled": { + "__name": "BINANCE_JOBS_TRAILING_TRADE_SELL_CONSERVATIVE_MODE_ENABLED", + "__description": "Set a boolean for selling at a trigger price reduced by the conservative ratio for each executed buy grid. You can use this feature in bear market conditions to secure smaller benefits over unreached higher gains. At least 2 buy trades must have been executed for the ratio to be applied.", + "__format": "boolean" + }, + "factor": { + "__name": "BINANCE_JOBS_TRAILING_TRADE_SELL_CONSERVATIVE_MODE_FACTOR", + "__description": "Set the conservative factor to be applied on sell trades with at least 2 executed buy grids.", + "__format": "number" + } } }, "system": { diff --git a/config/default.json b/config/default.json index 1fc19e30..fffa98f6 100644 --- a/config/default.json +++ b/config/default.json @@ -144,6 +144,10 @@ "whenSell": false, "whenStrongSell": false } + }, + "conservativeMode": { + "enabled": false, + "factor": 0.5 } }, "system": { diff --git a/migrations/1672576246043-flush-configuration-cache.js b/migrations/1672576246043-flush-configuration-cache.js new file mode 100644 index 00000000..2688f610 --- /dev/null +++ b/migrations/1672576246043-flush-configuration-cache.js @@ -0,0 +1,19 @@ +const path = require('path'); +const { logger: rootLogger, cache } = require('../app/helpers'); + +module.exports.up = async () => { + const logger = rootLogger.child({ + gitHash: process.env.GIT_HASH || 'unspecified', + migration: path.basename(__filename) + }); + + logger.info('Start migration'); + + cache.hdelall('trailing-trade-configurations:*'); + + logger.info('Finish migration'); +}; + +module.exports.down = next => { + next(); +}; diff --git a/public/js/CoinWrapperSellSignal.js b/public/js/CoinWrapperSellSignal.js index 4e2f1368..686a99f6 100644 --- a/public/js/CoinWrapperSellSignal.js +++ b/public/js/CoinWrapperSellSignal.js @@ -160,9 +160,13 @@ class CoinWrapperSellSignal extends React.Component {
- - > Trigger price ( - {(parseFloat(grid.triggerPercentage - 1) * 100).toFixed(2)} + + > {sell.conservativeModeApplicable ? 'Reduced' : ''}{' '} + Trigger price ( + {(parseFloat(sell.triggerPercentage - 1) * 100).toFixed(2)} %):
@@ -187,45 +191,41 @@ class CoinWrapperSellSignal extends React.Component { )} {grid.executed && grid.executedOrder.currentGridTradeIndex === i ? ( -
-
- - - Sold date: - -
- {moment(grid.executedOrder.transactTime).format('YYYY-MM-DD HH:mm')} -
+
+
+ - Sold date: +
+ {moment(grid.executedOrder.transactTime).format( + 'YYYY-MM-DD HH:mm' + )}
-
- - - Sold price: - -
- {parseFloat(grid.executedOrder.price).toFixed(precision)} -
+
+
+ - Sold price: +
+ {parseFloat(grid.executedOrder.price).toFixed(precision)}
-
- - - Sold qty: - -
- {parseFloat(grid.executedOrder.executedQty)} -
+
+
+ - Sold qty: +
+ {parseFloat(grid.executedOrder.executedQty)}
-
- - - Sold amount: - -
- {parseFloat(grid.executedOrder.cummulativeQuoteQty).toFixed(precision)} -
+
+
+ - Sold amount: +
+ {parseFloat(grid.executedOrder.cummulativeQuoteQty).toFixed( + precision + )}
+
) : ( - '' + '' )}
- {((grid.triggerPercentage - 1) * 100).toFixed(2)}% + {((sell.triggerPercentage - 1) * 100).toFixed(2)}%
@@ -275,7 +275,19 @@ class CoinWrapperSellSignal extends React.Component {
- Sell Signal{' '} + {symbolConfiguration.sell.conservativeMode.enabled && + symbolConfiguration.sell.enabled ? ( + + Conservative Sell ( + {( + (1 - symbolConfiguration.sell.conservativeMode.factor) * + 100 + ).toFixed(0)} + %){' '} + + ) : ( + Sell Signal + )} {symbolConfiguration.sell.enabled ? ( @@ -380,7 +392,19 @@ class CoinWrapperSellSignal extends React.Component {
- Sell Signal{' '} + {symbolConfiguration.sell.conservativeMode.enabled && + symbolConfiguration.sell.enabled ? ( + + Conservative Sell ( + {( + (1 - symbolConfiguration.sell.conservativeMode.factor) * + 100 + ).toFixed(0)} + %){' '} + + ) : ( + Sell Signal + )} {symbolConfiguration.sell.enabled ? ( diff --git a/public/js/SettingIcon.js b/public/js/SettingIcon.js index 85f6cfa0..8bbff624 100644 --- a/public/js/SettingIcon.js +++ b/public/js/SettingIcon.js @@ -1310,6 +1310,138 @@ class SettingIcon extends React.Component {
+ +
+ + + + + Conservative mode + + + + +
+
+ + + + + Reduce the sell trigger price + proportionally to the number of + executed buy grids - applies only to + grids with at least 2 executed buy + trades{' '} + + + If enabled, the bot will + sell at a trigger price + reduced by the conservative + ratio for each executed buy + grid. You can use this + feature in bear market + conditions to secure smaller + benefits over unreached + higher gains. At least 2 buy + trades must have been + executed for the ratio to be + applied. + + + }> + + + + + +
+
+ + + Conservative ratio{' '} + + + Set the conservative factor to + be applied on sell trades with + at least 2 executed buy grids. + i.e. if set to{' '} + 0.90, your + current grid sell percentage + will be reduced by{' '} + 10% for each + executed buy grid (except the + first one). For example, if + your sell trigger percentage + is 1.10, and you + have 3 executed buy grids, the + sell order trigger will be{' '} + 1.081. Remember + the sell trigger is not + modified if you have only 1 + executed buy grid. + + + }> + + + + + +
+
+
+
+
+
+
diff --git a/public/js/SymbolSettingIcon.js b/public/js/SymbolSettingIcon.js index 34364d49..106582fd 100644 --- a/public/js/SymbolSettingIcon.js +++ b/public/js/SymbolSettingIcon.js @@ -1184,6 +1184,138 @@ class SymbolSettingIcon extends React.Component {
+ +
+ + + + + Conservative mode + + + + +
+
+ + + + + Reduce the sell trigger price + proportionally to the number of + executed buy grids - applies only to + grids with at least 2 executed buy + trades{' '} + + + If enabled, the bot will + sell at a trigger price + reduced by the conservative + ratio for each executed buy + grid. You can use this + feature in bear market + conditions to secure smaller + benefits over unreached + higher gains. At least 2 buy + trades must have been + executed for the ratio to be + applied. + + + }> + + + + + +
+
+ + + Conservative ratio{' '} + + + Set the conservative factor to + be applied on sell trades with + at least 2 executed buy grids. + i.e. if set to{' '} + 0.90, your + current grid sell percentage + will be reduced by{' '} + 10% for each + executed buy grid (except the + first one). For example, if + your sell trigger percentage + is 1.10, and you + have 3 executed buy grids, the + sell order trigger will be{' '} + 1.081. Remember + the sell trigger is not + modified if you have only 1 + executed buy grid. + + + }> + + + + + +
+
+
+
+
+
+